RelatedObjectDoesNotExist during form validation - django

I have an issue that makes me crazy
I have model
class Property1(CommonInfo):
unit = models.ForeignKey(Unit)
is_true = models.BooleanField(default=False)
propertytype = models.ForeignKey(Propertytype, related_name='propertytype')
date = models.DateTimeField(null=True, blank=True)
followup_date = models.DateTimeField(null=True, blank=True)
quantity = models.PositiveSmallIntegerField()
def __str__(self):
return self.propertytype
def clean(self):
model = self.__class__
if (self.unit) and model.objects.filter(unit=self.unit, propertytype=self.propertytype ).exclude(id=self.id).count() == 1:
raise ValidationError('Same property cant be assigned more then ones')
for this model I have form
class Property1Form(forms.ModelForm):
class Meta:
model = Property1
fields = ['unit','propertytype','is_true','date','followup_date','quantity','description']
def __init__(self, *args, **kwargs):
super(Property1Form, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance:
self.fields['unit'].required = False
self.fields['unit'].widget.attrs['disabled'] = 'disabled'
And I have a view
def property_new(request,pk,uri):
unit = get_object_or_404(Unit, pk=pk)
title = 'property'
uri = _get_redirect_url(request, uri)
if request.method == "POST":
form = Property1Form(request.POST)
form.unit = unit
if form.is_valid():
properties = form.save(commit=False)
properties.unit = unit
properties.save()
messages.add_message(request, messages.SUCCESS, str(properties.unit) + "-SUCCESS Object created sucssefully")
return redirect(uri)
else:
form = Property1Form(initial={'unit': unit})
return render(request, 'object_edit.html', {'form': form, 'title':title, 'extend': EXTEND})
However, after creating new property I always get
RelatedObjectDoesNotExist at *** Property1 has no unit.
Error triggered during execution of
if form.is_valid():
What is the problem?
UPDATE:
Traceback:
File "C:\Users\Boris\dev\rentout\virtrentout\lib\site-packages\django\core\handlers\base.py" in get_response
132. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\Boris\dev\rentout\rentout\unit\views.py" in property_new
390. if form.is_valid():
File "C:\Users\Boris\dev\rentout\virtrentout\lib\site-packages\django\forms\forms.py" in is_valid
184. return self.is_bound and not self.errors
File "C:\Users\Boris\dev\rentout\virtrentout\lib\site-packages\django\forms\forms.py" in errors
176. self.full_clean()
File "C:\Users\Boris\dev\rentout\virtrentout\lib\site-packages\django\forms\forms.py" in full_clean
394. self._post_clean()
File "C:\Users\Boris\dev\rentout\virtrentout\lib\site-packages\django\forms\models.py" in _post_clean
430. self.instance.full_clean(exclude=exclude, validate_unique=False)
File "C:\Users\Boris\dev\rentout\virtrentout\lib\site-packages\django\db\models\base.py" in full_clean
1132. self.clean()
File "C:\Users\Boris\dev\rentout\rentout\unit\models.py" in clean
117. if (self.unit) and model.objects.filter(unit=self.unit, propertytype=self.propertytype ).exclude(id=self.id).count() == 1:
File "C:\Users\Boris\dev\rentout\virtrentout\lib\site-packages\django\db\models\fields\related.py" in __get__
608. "%s has no %s." % (self.field.model.__name__, self.field.name)
Exception Type: RelatedObjectDoesNotExist at /unit/property/new/6/http://127.0.0.1:8000/unit/property_details/6/
Exception Value: Property1 has no unit.

Setting required=False and disabling the widget is not ideal. Disabling the widget means that your browser will not submit any values for that field. Since you have required=False, this means that the form will set the foreign key to None. However, this is incompatible with the model's foreign key, which has null=False by default.
I'm not familiar enough with the internals to precisely explain the exception, but the basic problem is that Django is trying to fetch the related unit from the database, after the form has set the foreign key to None.
One work around is to check self.unit_id rather than self.unit. This prevents the database lookup, so you don't get the exception.
However, this seems a bit dirty to me. If you don't want the user to edit the unit field, I would remove it from the form completely. Check self.instance.pk to determine whether or not the instance is in the database already.
def __init__(self, *args, **kwargs):
super(Property1Form, self).__init__(*args, **kwargs)
if self.instance.pk:
del self.fields['unit']
Then in your view, you shouldn't have to set properties.unit = unit any more. I would remove form.unit = unit as well, I don't think it was ever useful.

Related

'invalid_choice' in field foreing key django

I have a problem with a foreing key field that I initialize with a queryset, the form is displayed but at the time of sending data the error 'in to_python raise ValidationError (self.error_messages [' invalid_choice '], code =' invalid_choice ')' appears' .
I know there is a way to invalidate the 'to_python' method but I still can not get it to work. I would appreciate any help.
My view:
class PlanesCreate(generic.CreateView):
model= Plan_Estudios
template_name= 'ControlEscolar/Administracion/planes/planes_form.html'
form_class= PlanForm
success_url = reverse_lazy('ControlEscolar:planes_filtro')
def get_form_kwargs(self):
kwargs = super(PlanesCreate, self).get_form_kwargs()
kwargs.update({'programa': self.request.session['programa']})
return kwargs
My form:
class PlanForm(forms.ModelForm):
class Meta:
model = Plan_Estudios
fields = "__all__"
widgets = {
'nombreplan': forms.TextInput(attrs={'class':'form-control'}),
'programa': forms.Select(attrs={'class':'form-control'}),
}
def __init__(self, *args, **kwargs):
p=kwargs.pop('programa', None)
super(PlanForm, self).__init__(*args, **kwargs)
query=Programa_Academico.objects.filter(pk=p)
self.fields['programa'].queryset=query #Inicialize field with query
My model:
class Plan_Estudios(models.Model):
nombreplan= models.CharField(max_length=30)
programa= models.ForeignKey(Programa_Academico, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.nombreplan
class Programa_Academico(models.Model):
nombreP= models.CharField(max_length=50)
siglas= models.CharField(max_length=10)
def __str__(self):
return self.siglas
Error:
File "C:\Users\Envs\py1\lib\site-packages\django\forms\forms.py" in is_valid
185. return self.is_bound and not self.errors
File "C:\Users\Envs\py1\lib\site-packages\django\forms\forms.py" in errors
180. self.full_clean()
File "C:\Users\Envs\py1\lib\site-packages\django\forms\forms.py" in full_clean
381. self._clean_fields()
File "C:\Users\Envs\py1\lib\site-packages\django\forms\forms.py" in _clean_fields
399. value = field.clean(value)
File "C:\Users\Envs\py1\lib\site-packages\django\forms\fields.py" in clean
147. value = self.to_python(value)
File "C:\Users\Envs\py1\lib\site-packages\django\forms\models.py" in to_python
1252.
raise ValidationError(self.error_messages['invalid_choice'],
code='invalid_choice')
Exception Type: KeyError at /ControlEscolar/planes/crear
Exception Value: 'invalid_choice'

Generic detail viewResult must be called with either an object pk or a slug

I got this error shown as title. According to document, I added "slug_url_kwarg = 'result'", (result is the app name). It doesn't work, then I added "def get_object(self):", it returns the error as "id is a KeyError"
The database it has created by itself a primary ket:"id", so I used this "id" as slug_field. Is this correct?
Do you think if it is necessary for me to use UpdateView? I want the html to display a result after submiting the form(using ajax)?
Thans in advance for your reply.
url
url(r'^result_list/$',ResultView.as_view(),name='result'),
views.py
class ResultView(UpdateView):
context_object_name = 'result_list'
template_name = 'result_list.html'
form_class = InputForm
slug_field = 'result.id' ---here means the pk,right?
slug_url_kwarg = 'result'
def get_queryset(self):
return Result.objects.all()
def post(self, request, *args, **kwargs):
form = InputForm(request.POST)
if form.is_valid():
if self.request.is_ajax():
company = form.cleaned_data['company']
region = form.cleaned_data['region']
queryset=Result.objects.filter(region=region)
return HttpResponse(simplejson.dumps(to_json),mimetype='application/json')
else:
return HttpResponse(form.errors)
def get_context_data(self, **kwargs):
context = super(ResultView, self).get_context_data(**kwargs)
context["sales"] = self.get_queryset().aggregate(Sum('sales'))
def get_object(self):
object = get_object_or_404(Result,id=self.kwargs['id']) <!here is not correct--->
return object
traceback
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\core\handlers\base.py" in get_response
132. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\views\generic\base.py" in view
71. return self.dispatch(request, *args, **kwargs)
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\views\generic\base.py" in dispatch
89. return handler(request, *args, **kwargs)
File "C:\Python27\lib\site-packages\django-1.8.3-py2.7.egg\django\views\generic\edit.py" in get
267. self.object = self.get_object()
File "C:\Users\user\Desktop\project\result\views.py" in get_object
54. object = get_object_or_404(Result,id=self.kwargs['id'])
Exception Type: KeyError at /result_list/
Exception Value: 'id'
You forgot to map the url pattern, you don't need to specify slug_field and slug_url_kwarg in your case:
url(r'^dupont_list/(?P<pk>[0-9]+)/$', DupontView.as_view(), name='dupont'),

Use multiple model in one modelform and save one user input field to two model

So,I was stuck on this for two days, I used to had a view and template that combine the django build-in User model and my own Maker model as below
Forms.py
from .models import Maker,Major,User
class NewMakerForm(forms.ModelForm):
password = forms.CharField()
username = forms.CharField()
def __init__(self ,*args, **kwargs):
super(NewMakerForm,self).__init__(*args, **kwargs)
self.fields['first_name'].required = True
self.fields['email'].required = True
class Meta:
model = User
fields = ('username','password','first_name','last_name','email')
class MakerProfileForm(forms.ModelForm):
#--------------------------------------error part------------------------
other_major = forms.CharField()
#--------------------------------------error part------------------------
class Meta:
model = Maker
fields = ('maker_major','other_major','maker_related_category','profile_pic',)
#--------------------------------------error part------------------------
def clean(self, *args, **kwargs):
super(MakerProfileForm,self).clean()
Major.objects.get_or_create(
major_name=self.cleaned_data.get('other_major')
)
self.cleaned_data['other_major']=Major.major_name
#--------------------------------------error part------------------------
It works fine at first, untail i add a new field and try to let user inpt some "other_major" and save the input to model Major's major_name row
Views.py
def NewMakerView(request):
registered = False
if request.method == 'POST':
user_form = NewMakerForm(data=request.POST)
profile_form = MakerProfileForm(data=request.POST,)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
profile = profile_form.save(commit=False)
profile.user = user
if 'profile_pic' in request.FILES:
profile.profile_pic = request.FILES['profile_pic']
profile.save()
registered = True
else:
print user_form.errors, profile_form.errors
else:
user_form = NewMakerForm()
profile_form = MakerProfileForm()
return render(request,
'boodemo/register.html',
{'user_form': user_form, 'profile_form': profile_form, 'registered': registered} )
I didn't change any code in Views.py (or shoudl I?)
And here's the error code, i don't get it why Major' has no attribute 'major_name
Traceback:
File "/home/ubuntu/virtualproject/venv/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
111. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/ubuntu/virtualproject/venv/bootsdemo/boodemo/views.py" in NewMakerView
114. if user_form.is_valid() and profile_form.is_valid():
File "/home/ubuntu/virtualproject/venv/lib/python2.7/site-packages/django/forms/forms.py" in is_valid
162. return self.is_bound and not bool(self.errors)
File "/home/ubuntu/virtualproject/venv/lib/python2.7/site-packages/django/forms/forms.py" in errors
154. self.full_clean()
File "/home/ubuntu/virtualproject/venv/lib/python2.7/site-packages/django/forms/forms.py" in full_clean
354. self._clean_form()
File "/home/ubuntu/virtualproject/venv/lib/python2.7/site-packages/django/forms/forms.py" in _clean_form
378. cleaned_data = self.clean()
File "/home/ubuntu/virtualproject/venv/bootsdemo/boodemo/forms.py" in clean
79. self.cleaned_data['other_major']=Major.major_name
Exception Type: AttributeError at /demo/newmaker/
Exception Value: type object 'Major' has no attribute 'major_name'
Major is a class. The class itself doesn't have that attribute, only the instances do. For some reason, you're getting or creating an instance, ignoring it, then trying to use a non-existent class attribute. Instead you should actually keep and use the instance you got.
major_instance, created = Major.objects.get_or_create(
major_name=self.cleaned_data.get('other_major')
)
self.cleaned_data['other_major'] = major_instance.major_name
SO, at last I figure out how to use multiple model in model form, and how to save it
English is not my mother language so I'll let the code talk
class Major(models.Model):
major_name = models.CharField(max_length=200)
def __unicode__(self):
return self.major_name
class Maker(models.Model):
...
maker_major = models.ManyToManyField(Major,blank=True)
...
def __unicode__(self):
return self.user.username
what i want to do here is let a user(Maker) choose a major(major_name).
class MakerProfileForm(forms.ModelForm):
other_major = forms.CharField()
class Meta:
model = Maker
fields = ('maker_major','other_major',...)
def clean(self, *args, **kwargs):
super(MakerProfileForm,self).clean()
major, _ = Major.objects.get_or_create(
major_name=self.cleaned_data.get('other_major')
)
self.cleaned_data['other_major'] = major.major_name
I use the ModelForm and model=Maker, but than i want to let user not only choose but can input text to add other_major into Major.major_name
if request.method == 'POST':
profile_form = MakerProfileForm(data=request.POST,)
if profile_form.is_valid():
profile = profile_form.save(commit=False)
profile.save()
profile_form.save_m2m()
major = Major.objects.get(major_name=profile_form.cleaned_data['other_major'])
profile.maker_major.add(major)
At last, save the maker_major choice by user, and add other_major witch is text input by user to MakerProfileForm

Django ModelForm Inheritance with request.user results in TypeError on __init__

Trying to inherit ModelForm to create a BaseForm to update created_by, modified_by fields in model.
My BaseForm is as
class BaseForm(ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('request', None).user
super(BaseForm, self).__init__(*args, **kwargs)
def save(self, *args, **kwargs):
kwargs['commit']=False
obj = super(BaseForm, self).save(*args, **kwargs)
if self.user:
obj.modified_by = self.user.username
obj.save()
return obj
Child Form is as
class InviteForm(BaseForm):
class Meta:
model = UserInvitation
and I am passing the request in child form through view
form = InviteForm(instance=instance,request=request)
however it gives error
TypeError at /accounts/invitation/
__init__() got an unexpected keyword argument 'request'
The error is not there if I do not inherit the BaseForm and write the code in child form i.e. InviteForm.
Please advise what I am doing wrong, I do not want to repeat the code in each form.
Traceback:
File "c:\Python27\lib\site-packages\django\core\handlers\base.py" in get_response
115. response = callback(request, *callback_args, **callback_kwargs)
File "c:\Python27\lib\site-packages\django\contrib\auth\decorators.py" in _wrapped_view
25. return view_func(request, *args, **kwargs)
File "C:\Python27\Lib\site-packages\django\bin\mysite\userplus\views.py" in invitation
87. form = InviteForm(instance=instance,request=request)
Exception Type: TypeError at /accounts/invitation/
Exception Value: __init__() got an unexpected keyword argument 'request'
To update the modified_by field that you have in your UserInvitation model, you should exclude it from being shown in the form:
class BaseForm(ModelForm):
...
class InviteForm(BaseForm):
class Meta:
model = UserInvitation
exclude = ('modified_by',)
Then in your view
form = InviteForm(request.POST, instance=instance)
if form.is_valid():
invite = form.save(commit=false)
invite.modified_by = request.user
invite.save()
That way the user making the request is used.
Alternatively, you can set the user beforehand:
# views.py
instance = Invite(modified_by=request.user)
form = InviteForm(request.POST, instance=instance)
...

AttributeError when saving MPTTModel

I'm trying to build a menu app in Django using django-mptt to create nested menu items. The menu items should be ordered by menu_order when the tree is built.
The problem is that whenever I add nested menu items, reorder them and save the menu, this error is raised:
'NoneType' object has no attribute 'tree_id'
To be able to save the menu I have to either manually rebuild the tree from Django shell, which does not always help, or remove the parent relation from the child items.
When removing order_insertion_by = ['menu_order'] from the MenuItem model, everything (except ordering) works as intended.
models.py:
class Menu(models.Model):
POSITIONS = Choices(('header', _('Header')), ('footer', _('Footer')))
title = models.CharField(max_length=255, default='')
position = models.SlugField(choices=POSITIONS, max_length=64, default='')
def save(self, *args, **kwargs):
MenuItem.objects.rebuild()
super(Menu, self).save(*args, **kwargs)
class MenuItem(MPTTModel):
content_type = models.ForeignKey(ContentType, blank=True, null=True)
object_id = models.PositiveIntegerField(blank=True, null=True)
linked_object = generic.GenericForeignKey()
menu = models.ForeignKey('Menu', related_name='menu_items')
parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
menu_order = models.PositiveSmallIntegerField(default=0)
class MPTTMeta:
order_insertion_by = ['menu_order']
admin.py:
class MenuItemInline(admin.StackedInline):
model = MenuItem
extra = 0
sortable_field_name = 'menu_order'
autocomplete_lookup_fields = {
'generic': [['content_type', 'object_id']]
}
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
field = super(MenuItemInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
if db_field.name == 'parent':
if request._obj_ is not None:
field.queryset = field.queryset.filter(menu=request._obj_)
else:
field.queryset = field.queryset.none()
return field
class MenuAdmin(admin.ModelAdmin):
inlines = (MenuItemInline,)
def get_form(self, request, obj=None, **kwargs):
request._obj_ = obj
return super(MenuAdmin, self).get_form(request, obj, **kwargs)
admin.site.register(Menu, MenuAdmin)
The traceback:
Traceback:
File "/.../django/core/handlers/base.py" in get_response
115. response = callback(request, *callback_args, **callback_kwargs)
File "/.../django/contrib/admin/options.py" in wrapper
372. return self.admin_site.admin_view(view)(*args, **kwargs)
File "/.../django/utils/decorators.py" in _wrapped_view
91. response = view_func(request, *args, **kwargs)
File "/.../django/views/decorators/cache.py" in _wrapped_view_func
89. response = view_func(request, *args, **kwargs)
File "/.../django/contrib/admin/sites.py" in inner
202. return view(request, *args, **kwargs)
File "/.../django/utils/decorators.py" in _wrapper
25. return bound_func(*args, **kwargs)
File "/.../django/utils/decorators.py" in _wrapped_view
91. response = view_func(request, *args, **kwargs)
File "/.../django/utils/decorators.py" in bound_func
21. return func(self, *args2, **kwargs2)
File "/.../django/db/transaction.py" in inner
223. return func(*args, **kwargs)
File "/.../django/contrib/admin/options.py" in change_view
1106. self.save_related(request, form, formsets, True)
File "/.../django/contrib/admin/options.py" in save_related
764. self.save_formset(request, form, formset, change=change)
File "/.../django/contrib/admin/options.py" in save_formset
752. formset.save()
File "/.../django/forms/models.py" in save
514. return self.save_existing_objects(commit) + self.save_new_objects(commit)
File "/.../django/forms/models.py" in save_existing_objects
634. saved_instances.append(self.save_existing(form, obj, commit=commit))
File "/.../django/forms/models.py" in save_existing
502. return form.save(commit=commit)
File "/.../django/forms/models.py" in save
370. fail_message, commit, construct=False)
File "/.../django/forms/models.py" in save_instance
87. instance.save()
File "/.../mptt/models.py" in save
794. self._tree_manager._move_node(self, rightmost_sibling, 'right', save=False)
File "/.../mptt/managers.py" in _move_node
414. self._make_sibling_of_root_node(node, target, position)
File "/.../mptt/managers.py" in _make_sibling_of_root_node
769. new_tree_id = getattr(right_sibling, self.tree_id_attr)
Exception Type: AttributeError at /admin/menus/menu/2/
Exception Value: 'NoneType' object has no attribute 'tree_id'
'NoneType' refers to right_sibling which is None.
The cause traces back to three lines above, where right_sibling is set:
right_sibling = target.get_next_sibling()
get_next_sibling returns None even though there is a next sibling.
When reordering the two last menu items, I sometimes end up with two root nodes with the same tree_id, lft, and rght values. This is causing the get_next_sibling function to query for a node where tree_id__gt=4 when the last two nodes both have a tree_id of 4.
The MenuItem objects are managed with an inline admin on every Menu object. They can be reordered using Grappelli's sortable inlines. There seems to be an issue with newly created child nodes getting a higher menu_order value than the following root nodes.
I'm using Python 2.7.4, Django 1.5.5, and django-mptt 0.6.0.
Is this a bug in django-mptt or am I doing something wrong?
I solved this by adding another sorting attribute and rebuilding the menu when the formset is saved. This is probably not the most optimal solution, every save now takes a few extra seconds.
models.py:
class MenuItem:
parent_order = models.PositiveSmallIntegerField(default=0)
def save(self, **kwargs):
if self.parent:
self.parent_order = self.parent.menu_order
super(MenuItem, self).save(**kwargs)
class MPTTMeta:
order_insertion_by = ['parent_order', 'menu_order']
admin.py:
class MenuAdmin(MarketRelatedAdmin):
def save_formset(self, request, form, formset, change):
formset.save()
MenuItem.objects.rebuild()
I solved this by adding two lines to the TreeManager.
def _make_sibling_of_root_node(self, node, target, position):
...
elif position == 'right':
if target_tree_id > tree_id:
new_tree_id = target_tree_id
lower_bound, upper_bound = tree_id, target_tree_id
shift = -1
else:
right_sibling = target.get_next_sibling()
if node == right_sibling:
return
# Addition
if not right_sibling:
return
# end of addition
new_tree_id = getattr(right_sibling, self.tree_id_attr)
lower_bound, upper_bound = new_tree_id, tree_id
shift = 1