#django #django-models #django-forms
#django #django-models #django-forms
Вопрос:
Я довольно новичок в Django и кодировании в целом, так что это может быть концептуальным упущением, но у меня заканчиваются идеи, поэтому любая помощь приветствуется.
Я пытаюсь добавить логику в метод clean () моей формы, который имеет вложенные блоки try. Я пытаюсь получить экземпляры объектов из разных моделей, связанных с ForeignKey, в каждом блоке try. Первые два уровня, похоже, работают нормально, но третий уровень выдает ошибку, приведенную ниже. Я напечатал значение и тип для wine_get.wine_id и получаю обратно 6 и ‘int’ соответственно, поэтому я не уверен, почему это не считается числом.
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/post/new/
Django Version: 2.1
Python Version: 3.6.5
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'debug_toolbar',
'bootstrap4',
'accounts',
'groups',
'posts']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware']
Traceback:
File "/anaconda3/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
34. response = get_response(request)
File "/anaconda3/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
126. response = self.process_exception_by_middleware(e, request)
File "/anaconda3/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
124. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/anaconda3/lib/python3.6/site-packages/django/views/generic/base.py" in view
68. return self.dispatch(request, *args, **kwargs)
File "/anaconda3/lib/python3.6/site-packages/django/contrib/auth/mixins.py" in dispatch
52. return super().dispatch(request, *args, **kwargs)
File "/anaconda3/lib/python3.6/site-packages/django/views/generic/base.py" in dispatch
88. return handler(request, *args, **kwargs)
File "/anaconda3/lib/python3.6/site-packages/django/views/generic/edit.py" in post
141. if form.is_valid():
File "/anaconda3/lib/python3.6/site-packages/django/forms/forms.py" in is_valid
185. return self.is_bound and not self.errors
File "/anaconda3/lib/python3.6/site-packages/django/forms/forms.py" in errors
180. self.full_clean()
File "/anaconda3/lib/python3.6/site-packages/django/forms/forms.py" in full_clean
382. self._clean_form()
File "/anaconda3/lib/python3.6/site-packages/django/forms/forms.py" in _clean_form
409. cleaned_data = self.clean()
File "/Users/evan/code/wine/wineProject/wineProject/posts/forms.py" in clean
78. wine = wine_get,
File "/anaconda3/lib/python3.6/site-packages/django/db/models/manager.py" in manager_method
82. return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/anaconda3/lib/python3.6/site-packages/django/db/models/query.py" in get
390. clone = self.filter(*args, **kwargs)
File "/anaconda3/lib/python3.6/site-packages/django/db/models/query.py" in filter
841. return self._filter_or_exclude(False, *args, **kwargs)
File "/anaconda3/lib/python3.6/site-packages/django/db/models/query.py" in _filter_or_exclude
859. clone.query.add_q(Q(*args, **kwargs))
File "/anaconda3/lib/python3.6/site-packages/django/db/models/sql/query.py" in add_q
1263. clause, _ = self._add_q(q_object, self.used_aliases)
File "/anaconda3/lib/python3.6/site-packages/django/db/models/sql/query.py" in _add_q
1287. split_subq=split_subq,
File "/anaconda3/lib/python3.6/site-packages/django/db/models/sql/query.py" in build_filter
1225. condition = self.build_lookup(lookups, col, value)
File "/anaconda3/lib/python3.6/site-packages/django/db/models/sql/query.py" in build_lookup
1096. lookup = lookup_class(lhs, rhs)
File "/anaconda3/lib/python3.6/site-packages/django/db/models/lookups.py" in __init__
20. self.rhs = self.get_prep_lookup()
File "/anaconda3/lib/python3.6/site-packages/django/db/models/fields/related_lookups.py" in get_prep_lookup
115. self.rhs = target_field.get_prep_value(self.rhs)
File "/anaconda3/lib/python3.6/site-packages/django/db/models/fields/__init__.py" in get_prep_value
965. return int(value)
Exception Type: TypeError at /post/new/
Exception Value: int() argument must be a string, a bytes-like object or a number, not 'ModelBase'
Локальные переменные из-за ошибки:
Variable Value
__class__ <class 'posts.forms.WineForm'>
post_type 'opened'
quantity 2
self <WineForm bound=True, valid=True, fields=(winemaker;wine;vintage;post_type;quantity;rating;location;tasting_notes)>
vintage '1950'
wine 'Cellar 2'
wine_get <Wine: Cellar 2>
winemaker 'Cellar 2'
winemaker_get <WineMaker: Cellar 2>
Данные сеанса:
Variable Value
'_auth_user_backend' 'django.contrib.auth.backends.ModelBackend'
'_auth_user_hash' '2c72b4192e2d568f616919e66da6b281d3764e4f'
'_auth_user_id' '2'
models.py
class WineMaker(models.Model):
winemaker_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, unique=True)
region = models.ForeignKey(
WineRegion,
db_column = 'region_id',
related_name = 'winemaker_region',
on_delete = models.CASCADE,
null = True
)
def __str__(self):
return self.name
class Meta:
db_table = 'winemakers'
class Wine(models.Model):
wine_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=510)
winemaker = models.ForeignKey(
WineMaker,
db_column = 'winemaker_id',
related_name = 'wine_winemakers',
on_delete = models.CASCADE
)
vintage = models.CharField(
max_length = 10,
choices = VINTAGES,
default = 'none'
)
description = models.TextField(max_length=3000, null=True)
style = models.ForeignKey(
Style,
db_column = 'style_id',
related_name = 'wine_styles',
on_delete = models.CASCADE,
null = True
)
varietal = models.ForeignKey(
Varietal,
db_column = 'varietal_id',
related_name = 'wine_varietals',
on_delete = models.CASCADE,
null = True
)
blend = models.ForeignKey(
Blend,
db_column = 'blend_id',
related_name = 'wine_blends',
on_delete = models.CASCADE,
null = True
)
def __str__(self):
return self.name
class Meta:
db_table = 'wines'
unique_together = ('winemaker', 'name', 'vintage')
class UserCellar(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name = 'userCellar_users',
on_delete = models.CASCADE
)
wine = models.ForeignKey(
Wine,
db_column = 'wine_id',
related_name = 'userCellar_wines',
on_delete = models.CASCADE
)
quantity = models.SmallIntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __int__(self):
return self.wine_id
def __str__(self):
return self.wine.name
class Meta:
db_table = 'user_cellars'
unique_together = ('user', 'wine')
...
forms.py
from django import forms
from .models import *
from django.contrib.auth import get_user_model
from .choices import VINTAGES, POST_TYPE_CHOICES
user = get_user_model()
class WineForm(forms.Form):
winemaker = forms.CharField(max_length=255)
wine = forms.CharField(max_length=510)
vintage = forms.ChoiceField(
choices = VINTAGES
)
post_type = forms.ChoiceField(
choices = POST_TYPE_CHOICES
)
quantity = forms.IntegerField(
required = False
)
rating = forms.IntegerField(
required = False
)
location = forms.CharField(
max_length = 255,
required = False
)
tasting_notes = forms.CharField(
max_length = 2000,
required = False,
widget = forms.Textarea
)
class Meta:
labels = {
'winemaker' : 'Winemaker',
'wine' : 'Wine',
'vintage' : 'Vintage',
'post_type' : 'Activity Type',
'rating' : 'Rating',
'location' : 'Location',
'tasting_notes' : 'Tasting Notes',
}
def __init__(self, *args, **kwargs):
super(WineForm, self).__init__(*args, **kwargs)
self.fields['quantity'].widget.attrs={
'id': 'quantity'
}
def clean(self):
super().clean()
winemaker = self.cleaned_data.get('winemaker')
wine = self.cleaned_data.get('wine')
vintage = self.cleaned_data.get('vintage')
post_type = self.cleaned_data.get('post_type')
quantity = self.cleaned_data.get('quantity')
if post_type and quantity:
# If both fields are valid
if post_type == 'opened':
try:
# Try to get the WineMaker instance
winemaker_get = WineMaker.objects.get(
name = winemaker,
)
try:
#Try to get the Wine instance
wine_get = Wine.objects.get(
name = wine,
winemaker = winemaker_get,
vintage = vintage,
)
try:
#Try to get the UserCellar instance
cellar_get = UserCellar.objects.get(
user = user,
wine = wine_get,
)
...
Комментарии:
1. где метод clean?
2. вы должны добавлять атрибут get
.id
всегда, когда вы выполняете объект get.3. Не вызывайте свои ForeignKeys
_id
(напримерregion_id
). Это не идентификаторы, это правильные ссылки на объекты, когда вы обращаетесь к ним в python. Фактически, Django автоматически добавляет поле_id
, чтобы вы могли получить идентификатор. Прямо сейчас, то, как вы его определили, еслиwinemaker
это экземпляр типаWineMaker
,winemaker.region_id
дает вамRegion
объект (не идентификатор) иwinemaker.region_id_id
дает вамid
Region
экземпляр. Не идеально.
Ответ №1:
Боюсь, что мой первоначальный ответ ниже — чушь собачья. При фильтрации по внешнему ключу Django не волнует, передаете ли вы объект или просто первичный ключ:
# wm is a WineMaker object; the next 2 lines are both valid
wines1 = Wine.objects.filter(winemaker=wm)
wines2 = Wine.objects.filter(winemaker=wm.id)
===================
Трассировка стека показывает, что проблема связана с этой строкой (хотя сообщение об ошибке, возможно, менее чем полезно):
wine_id = wine_get.wine_id
Поскольку wine_id
это внешний ключ, вам необходимо передать Wine
экземпляр:
wine_id = wine_get
Комментарии:
1. Изначально я настроил его таким образом, чтобы я мог более явно управлять соглашениями об именовании полей БД при просмотре таблиц через клиент БД. Точка зрения взята на ремонтопригодность. Что касается изменения ссылки на экземпляр объекта, а не только на атрибут wine_get.wine_id, я попробовал это, и я получаю то же самое результирующее сообщение об ошибке. Поскольку я использую тот же синтаксис для wine_get, что и для вложенного cellar_get, и только cellar_get выдает ошибку, возможно ли, что проблема связана с тем, что cellar_get вложен на 3 уровня глубже? Ссылка на ModelBase также сбивает меня с толку.
2. Обновленные модели и форма выше.
3.
id
в DB это так же ясно и просто, как в коде Python. Где вы можете получить некоторую ясность на уровне базы данных, так это с помощью имен таблиц вручную, где Django по умолчанию добавляет название приложения. Я собираюсь еще раз взглянуть на код, касающийся фактической ошибки.4. Я в тупике. Перечисленные вами локальные переменные не включают
user
; является ли это допустимым экземпляром пользователя?5. Да, запуск средств отладки показывает действительный сеанс пользователя. Я добавил их выше. Спасибо за ваш отзыв. Я продолжу копать.