Ошибка целостности (поле ограничения FOREIGN KEY) возникает при попытке сохранить request.POST из формы модели
Я работаю над коммерческим приложением (с django, но очень новичок в этом), где пользователь может создать объявление через ModelForm под названием ListingForm, которое наследуется от модели под названием Listing.Вот код для модели Listing и ListingForm:
from django.contrib.auth.models import AbstractUser
from django.db import models
class Listing(models.Model):
NAME_CHOICES = [
('Fashion', 'Fashion'),
('Toys','Toys'),
('Electronic','Electronics'),
('Home', 'Home'),
('Other', 'Other')
]
import datetime
title = models.CharField(max_length= 64)
date_made = models.DateField(default= datetime.date.today())
description = models.TextField()
user = models.ForeignKey(User, to_field='username', on_delete=models.CASCADE, related_name='user_listings', null=True)
starting_bid = models.DecimalField(decimal_places=2, max_digits=264, default=10.00)
image_url = models.CharField(blank=True, max_length=1000)
category = models.ForeignKey(Category, on_delete=models.CASCADE, to_field='name', related_name='category_listings', default=NAME_CHOICES[4][0])
listing_category = models.CharField(max_length=12, choices=NAME_CHOICES, null=True, default=NAME_CHOICES[4][0])
is_active = models.BooleanField(default=True)
def __str__(self):
return f'{self.title}'
Вот код для ``ListingForm'':
from .models import Listing
from django.forms import ModelForm
class ListingForm(ModelForm):
class Meta:
model = Listing
exclude = [
'date_made',
'user',
'category',
'is_active',
]
Модель Listing также имеет внешние ключи к и внутри моделей User, Category и Bid.
Вот код для этих моделей (те же импорты, что и для модели Listing):
class User(AbstractUser):
def __str__(self):
return f'{self.username} '
class Category(models.Model):
NAME_CHOICES = [
('Fashion', 'Fashion'),
('Toys','Toys'),
('Electronic','Electronics'),
('Home', 'Home'),
('Other', 'Other')
]
name = models.CharField(max_length=12, choices= NAME_CHOICES, unique=True)
def __str__(self):
return self.name
class Bid(models.Model):
value = models.DecimalField(decimal_places=2, max_digits=256)
listing = models.ForeignKey('Listing', on_delete=models.CASCADE, related_name='bid_listings', default=20.00)
def __str__(self):
return f'{self.value}'
Я хочу, чтобы пользователь мог создать листинг через представление, основанное на функциях:
def create_listing(request):
if request.method == 'POST':
import datetime
listing_form = ListingForm(request.POST)
bid = request.POST['starting_bid']
if listing_form.is_valid():
listing_form.save(commit=False)
listing_form.user = request.user
listing_form.date_made = datetime.datetime.today()
listing_form.is_active = True
listing_form.save()
Bid.objects.create(value=bid, listing=listing_form)
return HttpResponse('Listing has been saved successfully!')
else:
listing_form = ListingForm()
return render(request, 'auctions/createlisting.html',{
'listing_form':listing_form
})
При попытке запустить py manage.py runserver в терминале, я получаю ошибку Integrity в /createlisting.
Вот отслеживание:
Traceback (most recent call last):
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\ACER\Desktop\Projects\commerce\auctions\views.py", line 84, in create_listing
listing_form.save()
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\forms\models.py", line 548, in save
self.instance.save()
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\db\models\base.py", line 806, in save
self.save_base(
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\db\models\base.py", line 857, in save_base
updated = self._save_table(
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\db\models\base.py", line 1000, in _save_table
results = self._do_insert(
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\db\models\base.py", line 1041, in _do_insert
return manager._insert(
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\db\models\query.py", line 1434, in _insert
return query.get_compiler(using=using).execute_sql(returning_fields)
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\db\models\sql\compiler.py", line 1632, in execute_sql
self.connection.ops.fetch_returned_insert_columns(
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\db\backends\base\operations.py", line 205, in fetch_returned_insert_columns
return cursor.fetchone()
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\db\utils.py", line 97, in inner
with self:
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\db\utils.py", line 91, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "C:\Users\ACER\AppData\Local\Programs\Python\Python310\lib\site-packages\django\db\utils.py", line 98, in inner
return func(*args, **kwargs)
django.db.utils.IntegrityError: FOREIGN KEY constraint failed
[10/May/2022 21:00:05] ←[35;1m"POST /createlisting HTTP/1.1" 500 135972←[0m
Как решить эту ошибку? Я подозреваю, что это как-то связано с уникальностью моих идентификаторов в одной из моих моделей, или их отсутствием.
Здравствуйте,
Итак, я начну с некоторых улучшений, которые вы должны назначить своему проекту, а затем погружусь в основную проблему.
Во-первых, в вашем models. py, вы можете изменить date_made с DateField на DateTimeField, чтобы вы могли сохранить время, в которое был создан ваш листинг, без необходимости импортировать библиотеку datetime и переопределить его в ваших представлениях, когда форма отправляется, добавив атрибут, который является auto_now_add:
...
date_made = models.DateTimeField(auto_now_add=True)
Эта статья может быть полезна, если вы хотите найти разницу между DateTimeField и DateField
https://www.jafoor.com/post/django-datetimefield-datefield/
Во-вторых, в вашем views.py вы можете просто взять вашу grid, исходящую из данных поста в вашей форме, используя метод cleaned_data, который поставляется с нашей формой, и из него мы можем взять все данные поста, и будет лучше, если вы поместите это внутрь if listing_form.is_valid():
...
if listing_form.is_valid():
bid = listing_form.cleaned_data['starting_bid']
Подробнее о методе cleaned_data читайте здесь: Что такое использование cleaned_data в Django
В-третьих, в вашем views.py, вы должны изменить атрибут listing в Bid.objects.create() с listing_form на listing_form. instance, instance здесь означает объект, который ваша форма поместит вместо того, чтобы назначить всю форму, и, конечно, это приведет к ошибке:
...
Bid.objects.create(value=bid, listing=listing_form.instance)
Теперь поговорим о вашей проблеме. Для ее решения сначала нужно присвоить атрибут полю category в классе Listing в вашем models.py, который называется db_constraint и мы установим его в False:
...
category = models.ForeignKey(Category, on_delete=models.CASCADE, to_field='name', related_name='category_listings', default=NAME_CHOICES[4][0], db_constraint=False)
затем в вашей cmd введите python manage.py makemigrations затем python manage.py migrate для переноса этого изменения в вашу базу данных
Затем, идите и зарегистрируйте модель Category в admin.py, чтобы вы могли создавать категории вручную из панели администратора для последующего назначения в вашей форме:
from .models import Category
admin.site.register(Category)
Затем перейдите и создайте свои категории в панели администратора.
В последнюю очередь перейдите в ваш views.py, и мы добавим категорию вашего листинга вручную, взяв поле listing_category из нашей формы с помощью функции cleaned_data и этим мы возьмем категорию, которая имеет то же имя, что и listing_category:
from .models import Listing, Bid, Category
...
if listing_form.is_valid():
...
listing_form.category = Category.objects.get(name=listing_form.cleaned_data['listing_category'])
...
- Если у вас есть другие вопросы, свяжитесь со мной в моем discord: Ramy#8162 .