Validating ModelChoiceField in Django forms - python

I'm trying to validate a form containing a ModelChoiceField:
forms.py:
from django import forms
from modelchoicetest.models import SomeObject
class SomeObjectAddForm(forms.ModelForm):
class Meta:
model = SomeObject
models.py:
from django.db import models
class SomeChoice(models.Model):
name = models.CharField(max_length=16)
def __unicode__(self):
return self.name
class SomeObject(models.Model):
choice = models.ForeignKey(SomeChoice)
views.py:
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from forms import SomeObjectAddForm
def add(request):
if request.method == 'POST':
form = SomeObjectAddForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('modelchoicetest_add'))
else:
form = SomeObjectAddForm()
return render_to_response('modelchoicetest/index.html',
{'form': form},
context_instance=RequestContext(request))
When it is used in normal circumstances, everything goes just fine. But I'd like to protect the form from the invalid input. It's pretty obvious that I must get forms.ValidationError when I put invalid value in this field, isn't it? But if I try to submit a form with a value 'invalid' in 'somechoice' field, I get
ValueError: invalid literal for int() with base 10: 'invalid'
and not the expected forms.ValidationError. What should I do? I tried to place a def clean_somechoice(self) to check this field but that didn't work: ValueError happens before it comes to clean_somechoice()
Plus I don't think this is a good solution, there must be something more simple but I just missed that.
here's the full traceback:
Traceback:
File "/usr/local/lib/python2.6/dist-packages/django/core/handlers/base.py" in get_response
101. response = callback(request, *callback_args, **callback_kwargs)
File "/home/andrey/public_html/example/modelchoicetest/views.py" in add
11. if form.is_valid():
File "/usr/local/lib/python2.6/dist-packages/django/forms/forms.py" in is_valid
120. return self.is_bound and not bool(self.errors)
File "/usr/local/lib/python2.6/dist-packages/django/forms/forms.py" in _get_errors
111. self.full_clean()
File "/usr/local/lib/python2.6/dist-packages/django/forms/forms.py" in full_clean
276. value = field.clean(value)
File "/usr/local/lib/python2.6/dist-packages/django/forms/fields.py" in clean
154. value = self.to_python(value)
File "/usr/local/lib/python2.6/dist-packages/django/forms/models.py" in to_python
911. value = self.queryset.get(**{key: value})
File "/usr/local/lib/python2.6/dist-packages/django/db/models/query.py" in get
330. clone = self.filter(*args, **kwargs)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/query.py" in filter
536. return self._filter_or_exclude(False, *args, **kwargs)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/query.py" in _filter_or_exclude
554. clone.query.add_q(Q(*args, **kwargs))
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/query.py" in add_q
1109. can_reuse=used_aliases)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/query.py" in add_filter
1048. connector)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/where.py" in add
66. value = obj.prepare(lookup_type, value)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/where.py" in prepare
267. return self.field.get_prep_lookup(lookup_type, value)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/fields/__init__.py" in get_prep_lookup
314. return self.get_prep_value(value)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/fields/__init__.py" in get_prep_value
496. return int(value)
Exception Type: ValueError at /
Exception Value: invalid literal for int() with base 10: 'invalid'

It looks to me like the exception is being raised by the clean method of the actual ModelChoiceField object. Because it's a foreign key, Django is expecting an int, which would be representative of the pk for SomeChoice. How exactly are you passing invalid into the form?
RESPONSE TO COMMENT
If you really feel you need to catch this, you can try overriding the default ModelChoiceField by creating a new field called choice and pass in the to_field_name kwarg into the ModelChoiceField __init__ method. This way Django won't be filtering on pk, and won't raise that exception.
Personally I wouldn't use this solution. There is no need to accommodate user who are hacking your form.

This is known Django bug:
http://code.djangoproject.com/ticket/11716
And while this bug is not fixed, you can only handle ValueError manually.

Related

Why do I get a context must be a dict rather than type

I've been working on a project for a while, and I have a resource called Item.
The item detail view can only be viewed, if the item is from the same company as the user. If not, it should be a 404. This is the code that I have:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# To only show items in your company
if (context['item'].company != getCompany(self.request.user)):
return HttpResponseNotFound
return context
getCompany is a function I wrote to check the users company. The company is in a custom Profile model. This function works, I already used it multiple times for other things
Now i expected to have a 404 when going to a item from another company, but instead this error appears:
Internal Server Error: /fr/items/5/
Traceback (most recent call last):
File "/Users/username/Documents/Work/Inventory/inventory-env/lib/python3.9/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
File "/Users/username/Documents/Work/Inventory/inventory-env/lib/python3.9/site-packages/django/core/handlers/base.py", line 220, in _get_response
response = response.render()
File "/Users/username/Documents/Work/Inventory/inventory-env/lib/python3.9/site-packages/django/template/response.py", line 114, in render
self.content = self.rendered_content
File "/Users/username/Documents/Work/Inventory/inventory-env/lib/python3.9/site-packages/django/template/response.py", line 92, in rendered_content
return template.render(context, self._request)
File "/Users/username/Documents/Work/Inventory/inventory-env/lib/python3.9/site-packages/django/template/backends/django.py", line 58, in render
context = make_context(
File "/Users/username/Documents/Work/Inventory/inventory-env/lib/python3.9/site-packages/django/template/context.py", line 278, in make_context
raise TypeError(
TypeError: context must be a dict rather than type.
Edited:
What did I miss?
The get_context_data(...) method supposed to be return a dict object. In your case, you are returning a HttpResponseNotFound which is not correct.
The easy method to raise the 404 error is to use the Http404 class to raise the exception
from django.http import Http404
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# To only show items in your company
if (context['item'].company != getCompany(self.request.user)):
raise Http404
return context
as Django doc
def get_context_data(self, **kwargs):
is used to
Returns a dictionary representing the template context. The keyword
arguments provided will make up the returned context
and you are trying to return HttpResponseNotFound, that will not work, you should return dict type
Rather raise a PermissionDenied error, then return a Response incorrectly as the context, it gives better meaning, then Django will return the usual 403 page.
from django.core.exceptions import PermissionDenied
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# To only show items in your company
if (context['item'].company != getCompany(self.request.user)):
raise PermissionDenied("You are not authorized to view the requested company")
return context

Models and ModelForms: needs to have a value for field "id" before this many-to-many relationship can be used

I'm using a Django Model with some many-to-many fields. I'm also using a ModelForm to generate the associated form. It is my understanding that, provided nothing else is overridden, Django should be able to handle many-to-many fields being saved in the ModelForm?
For me, attempting to do this is causing this error:
Internal Server Error: /cameramodel/create/
Traceback (most recent call last):
File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py", line 71, in view
return self.dispatch(request, *args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/django/contrib/auth/mixins.py", line 52, in dispatch
return super().dispatch(request, *args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py", line 97, in dispatch
return handler(request, *args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/django/views/generic/edit.py", line 172, in post
return super().post(request, *args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/django/views/generic/edit.py", line 141, in post
if form.is_valid():
File "/usr/local/lib/python3.8/site-packages/django/forms/forms.py", line 185, in is_valid
return self.is_bound and not self.errors
File "/usr/local/lib/python3.8/site-packages/django/forms/forms.py", line 180, in errors
self.full_clean()
File "/usr/local/lib/python3.8/site-packages/django/forms/forms.py", line 383, in full_clean
self._post_clean()
File "/usr/local/lib/python3.8/site-packages/django/forms/models.py", line 403, in _post_clean
self.instance.full_clean(exclude=exclude, validate_unique=False)
File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 1188, in full_clean
self.clean()
File "./schema/models.py", line 1439, in clean
if self.metering_modes is True:
File "/usr/local/lib/python3.8/site-packages/django/db/models/fields/related_descriptors.py", line 527, in __get__
return self.related_manager_cls(instance)
File "/usr/local/lib/python3.8/site-packages/django/db/models/fields/related_descriptors.py", line 838, in __init__
raise ValueError('"%r" needs to have a value for field "%s" before '
ValueError: "<CameraModel: Canon Canonflex R2000>" needs to have a value for field "id" before this many-to-many relationship can be used.
So I checked out the docs and it seems like I need to override the save method in the ModelForm. So I did this:
class CameraModelForm(ModelForm):
class Meta:
model = CameraModel
fields = '__all__'
def save(self, commit=True):
form = super(CameraModelForm, self).save(commit=False)
if commit:
form.save(commit=False)
form.save_m2m()
return form
and instead I'm getting a different error:
TypeError at /cameramodel/create/
save() got an unexpected keyword argument 'commit'
Request Method: POST
Request URL: http://localhost:8000/cameramodel/create/
Django Version: 2.2.12
Exception Type: TypeError
Exception Value:
save() got an unexpected keyword argument 'commit'
Exception Location: /home/jonathan/git/camerahub/schema/models.py in save, line 1397
and models.py:1397 is actually an overridden Model save to add a slug field:
class CameraModel(models.Model):
# other stuff here
def save(self, *args, **kwargs):
if not self.slug:
custom_slugify_unique = UniqueSlugify(
unique_check=cameramodel_check, to_lower=True)
self.slug = custom_slugify_unique("{} {} {}".format(
self.manufacturer.name, self.model, str(self.disambiguation or '')))
super().save(*args, **kwargs)
For what it's worth, I am using many-to-many fields in some other models and I'm not seeing a problem with them. I'm a Python/Django beginner though and I'm pretty stuck on this one because simply following the advice on a million other posts like this one hasn't helped me. Grateful for any advice or code snippets anyone can offer.
If anyone needs more context, the whole project is open source and this branch is available here: https://github.com/djjudas21/camerahub/tree/278c_m2m_error
If you override, the save method, it means you want to make some checks/changes before saving your form. I would instead do:
def save(self):
form = super(CameraModelForm, self).save(commit=False)
if ...: # some condition on form values
form.save() # remove the commit here
form.save_m2m()
return form

passing args and kwargs to parent class with extra content in django CreateView

I've made a django form in which I want to pass some context I need to display (mainly database entries in tags so the user can choose from them). I therefor made a get_context_data function where I add the context to the existing context like this:
def get_context_data(self, **kwargs):
context = super(UploadView, self).get_context_data(**kwargs)
context['categories'] = Category.objects.all()
context['form'] = VideoForm
return context
however, the form is not saving the information passed to the database. Why would that not work?
Here is part of my code!
forms.py:
class VideoForm(forms.ModelForm):
category = forms.ModelChoiceField(queryset=Category.objects.all(), empty_label=None)
class Meta:
model = Video
fields = [
'title',
'description',
'description_short',
'category',
'time',
'thumbnail',
'type',
]
def clean_thumbnail(self):
picture = self.cleaned_data.get("thumbnail")
if not picture:
raise forms.ValidationError("No Image")
else:
w, h = get_image_dimensions(picture)
if w/h != (16/9):
raise forms.ValidationError("Image in wrong aspect ratio (should be 16:9)")
return picture
upload.html (it's pretty long so it's better to upload it to Pastebin)
views.py:
class UploadView(LoginRequiredMixin, CreateView):
form_class = VideoForm
template_name= 'upload.html'
def form_valid(self, form):
instance = form.save(commit=False)
instance.uploader=self.request.user
return super(UploadView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(UploadView, self).get_context_data(**kwargs)
context['categories'] = Category.objects.all()
context['form'] = VideoForm
return context
I'm using a custom form so I can set classes which I use for editing the CSS (instead of just using form.as_p and having a really ugly from there...)
EDIT:
After a bit more testing I found that if I put print(instance) inside the def form_valid(self, form): function it would not print out anything, suggesting the instance is empty. How can this be? Also, I tried removing: context['form'] = VideoForm and no errors show up but if I fill in the form correctly it still doesn't work!
EDIT 2:
I created a 'form_invalid' function like this:
def form_invalid(self, form):
print(form.instance)
return super(UploadView, self).form_invalid()
which causes:
TypeError: form_invalid() missing 1 required positional argument: 'form'
This had me thinking as I am not receiving any errors when I get redirected back to the form after submitting. Here is the form I wrote: https://pastebin.com/3H6VRZR1
Also I tried testing it with just form.as_p which causes the same problem
EDIT 3:
After testing some of the answers we found out that the form of the HTML page itself is probably the cause as the form arrived in form_invalid() completely empty. I decided that I would try to use form.as_p again see if it still caused the same issue as my custom form. Now I'm getting a new error:
null value in column "category_id" violates not-null constraint
DETAIL: Failing row contains (8, test new fom reg, test-new-fom-reg, null, test, test, 2018-01-10 13:44:58.81876+00, 2018-01-10 13:44:58.818789+00, 1, thumbnails/test-new-fom-reg.png, 2, 1, /home/trie/Desktop/django/vidmiotest/media/videos/test.mp4).
with:
USER
admin
GET
No GET data
POST
Variable Value
title 'test new fom reg'
category '8'
type '1'
time '1'
description 'test'
csrfmiddlewaretoken `BeizxWHU5KDbixit9vpxKoxEeBxgU9MNITaNlkM1qtI0Aq6kIThHrtjfUsQXjxON'
description_short 'test'
FILES
Variable Value
thumbnail <TemporaryUploadedFile: sixteentonineratio.png (image/jpeg)>
videoFile <TemporaryUploadedFile: test 3.mp4 (video/mp4)>
as the data the data send from the form which suggest (based on this) that category_id is not in my model for the form. which it is (the field is just called category), but why would it think it should be in there?
I just checked phppgadmin to see what the database looked like and there the field is called id_category while in my model it's called category, why?
EDIT 4: I never added the Traceback for the error above:
Internal Server Error: /upload/
Traceback (most recent call last):
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 85, in _execute
return self.cursor.execute(sql, params)
psycopg2.IntegrityError: null value in column "category_id" violates not-null constraint
DETAIL: Failing row contains (12, test, test, null, test, test, 2018-01-16 18:18:25.907513+00, 2018-01-16 18:18:25.907538+00, 6, thumbnails/test_d1MHjMX.png, 2, 1, /home/trie/Desktop/django/vidmiotest/media/videos/test.mp4).
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/core/handlers/exception.py", line 35, in inner
response = get_response(request)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/core/handlers/base.py", line 128, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/core/handlers/base.py", line 126, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/views/generic/base.py", line 69, in view
return self.dispatch(request, *args, **kwargs)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/contrib/auth/mixins.py", line 52, in dispatch
return super().dispatch(request, *args, **kwargs)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/views/generic/base.py", line 89, in dispatch
return handler(request, *args, **kwargs)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/views/generic/edit.py", line 172, in post
return super().post(request, *args, **kwargs)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/views/generic/edit.py", line 142, in post
return self.form_valid(form)
File "/home/trie/Desktop/django/vidmiotest/upload/views.py", line 21, in form_valid
instance.save()
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/base.py", line 729, in save
force_update=force_update, update_fields=update_fields)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/base.py", line 759, in save_base
updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/base.py", line 842, in _save_table
result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/base.py", line 880, in _do_insert
using=using, raw=raw)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/query.py", line 1125, in _insert
return query.get_compiler(using=using).execute_sql(return_id)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/models/sql/compiler.py", line 1280, in execute_sql
cursor.execute(sql, params)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 100, in execute
return super().execute(sql, params)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 68, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 85, in _execute
return self.cursor.execute(sql, params)
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/utils.py", line 89, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/home/trie/Desktop/django/venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 85, in _execute
return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: null value in column "category_id" violates not-null constraint
DETAIL: Failing row contains (12, test, test, null, test, test, 2018-01-16 18:18:25.907513+00, 2018-01-16 18:18:25.907538+00, 6, thumbnails/test_d1MHjMX.png, 2, 1, /home/trie/Desktop/django/vidmiotest/media/videos/test.mp4).
The problem in the question is that super().form_valid() is called and overwriting the constructed form with context['form'] = VideoForm.
Let the framework setup the form when using CreateView.
The inherited CreateView provides the functionality to setup the form, save the form, set self.object on the view, and redirect to a success url.
I.e CreateView.form_valid provides:
self.object = form.save()
The solution is correct to set the uploader in UploadView, but calling the super.form_valid will try to save the form again.
As I understand the desired behaviour is:
set uploader
save the object
redirect to success url
in code:
instance = form.save(commit=False)
instance.uploader = self.request.user
instance.save()
return redirect(self.get_success_url()) # Skip the call to super
Also as pointed out in other answer, the context['form'] = VideoForm will overwrite the form setup by the CreateView.
I suggest to look how the execution flow works for the CreateView, it will setup the form for the template context both in GET and POST situation.
One other way to solve it is to allow the VideoForm to accept uploader in init:
class VideoForm(forms.ModelForm):
def __init__(self, uploader, *args, **kwargs):
self.uploader = uploader
super().__init__(*args, **kwargs)
def save(self, commit=True):
instance = super().save(commit=False)
instance.uploader = self.uploader
if commit:
instance.save()
return instance
and provide the uploader to the form
class UploadView(..., CreateView):
def get_form_kwargs(self):
kwargs= super().get_form_kwargs()
kwargs['uploader'] = self.request.user
return kwargs
Hope it helps.
Looking at EDIT 3:
The reason it's say 'category_id' is that foreign keys in a django model will automatically get suffixed with '_id' as column name in the database. Documentation.
The Video model is likely to have a foreign key to Category.
If you find id_category in database, you might have gone out of sync with your migrations/models? You should probably try clear the database and/or to re-run makemigrations/migrate
You certainly don't need that context['form'] = VideoForm line. Apart from anything else, it's overwriting the existing form so that your template will never see any errors.

using request.user.is_authenticated() in Django project

When I try to do request.user.is_authenticated() I get a ValidationError: None is not a valid ObjectId
I'm trying to track down the problem but I do not what's causing it.I'm using MongoEngine (and MongoDB.)
I have the following in my settings.py:
AUTHENTICATION_BACKENDS = (
'mongoengine.django.auth.MongoEngineBackend',
'rs.claimutil.auth_backend.ClaimAuthBackend',
)
SESSION_ENGINE = 'mongoengine.django.sessions'
This is what I get:
Traceback: File
"/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/core/handlers/base.py"
in get_response
111. response = callback(request, *callback_args, **callback_kwargs) File "/Users/bastiano/Documents/ttsf/rsrv/views.py" in reserve
11. if not request.user.is_authenticated(): File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/utils/functional.py"
in inner
184. self._setup() File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/utils/functional.py"
in _setup
248. self._wrapped = self._setupfunc() File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/contrib/auth/middleware.py"
in
16. request.user = SimpleLazyObject(lambda: get_user(request)) File
"/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/contrib/auth/middleware.py"
in get_user
8. request._cached_user = auth.get_user(request) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/contrib/auth/init.py"
in get_user
101. user = backend.get_user(user_id) or AnonymousUser() File
"/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mongoengine/django/auth.py"
in get_user
149. return User.objects.with_id(user_id) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mongoengine/queryset.py"
in with_id
923. return self.filter(pk=object_id).first() File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mongoengine/queryset.py"
in first
843. result = self[0] File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mongoengine/queryset.py"
in getitem
1136. return self._document._from_son(self._cursor[key]) File
"/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mongoengine/queryset.py"
in _cursor
579. self._cursor_obj = self._collection.find(self._query, File
"/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mongoengine/queryset.py"
in _query
375. self._mongo_query = self._query_obj.to_query(self._document) File
"/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mongoengine/queryset.py"
in to_query
202. query = query.accept(QueryCompilerVisitor(document)) File
"/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mongoengine/queryset.py"
in accept
267. return visitor.visit_query(self) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mongoengine/queryset.py"
in visit_query
159. return QuerySet._transform_query(self.document, **query.query) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mongoengine/queryset.py"
in _transform_query
720. value = field.prepare_query_value(op, value) File
"/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mongoengine/base.py"
in prepare_query_value
455. return self.to_mongo(value) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mongoengine/base.py"
in to_mongo
451. self.error(unicode(e)) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/mongoengine/base.py"
in error
203. raise ValidationError(message, errors=errors, field_name=field_name)
Exception Type: ValidationError at /rs/claim Exception Value: None is
not a valid ObjectId
Any ideas why this is happening? Is there an easier way to do user authentication in Django + MongoDB?
Views.py:
def claim(request):
if request.method == 'GET':
if not request.user.is_authenticated():
return shortcuts.redirect('rs/login')
all_b = dbutil.get_b(all=True)
return shortcuts.render_to_response('rs/index.html',
{'all_b':all_b},
context_instance=template.RequestContext(request))
elif request.method == 'POST':
The rest of the view is omitted for simplicity. I used ipdb to debug it and if not request.user.is_authenticated() is the problem. I tried using django.contrib.auth.decorators.login_required.decorator before, but it, too, failed.
Try to update your mongoengine to the latest version. In master, it is
def get_user(userid):
"""Returns a User object from an id (User.id). Django's equivalent takes
request, but taking an id instead leaves it up to the developer to store
the id in any way they want (session, signed cookie, etc.)
"""
# your installed mongoengine might not include following two lines
if not userid:
return AnonymousUser()
return MongoEngineBackend().get_user(userid) or AnonymousUser()
A userid w/ value of None causes the problem, according to the trackback.
What version of MongoEngine / Django are you using?
Have you enabled the django.contrib.auth.middleware.AuthenticationMiddleware? That should set a User instance or AnonymousUser to the request.
What does rs.claimutil.auth_backend.ClaimAuthBackend look like? And what does its get_user method return? Does it stop erroring if you just have one authentication backend?
In one of my views I had the following:
r_v.obj.backend = 'mongoengine.django.auth.MongoEngineBackend', which is why Django was ignoring the AUTHENTICATION_BACKENDS in settings.py, and was never using my custom authentication backend.

Django form.is_valid keeps throwing KeyError

I have this code in my view:
def add_intern(request):
if request.method == 'POST':
form = InternApplicationForm(request.POST)
if form.is_valid():
form.save()
form = InternApplicationForm()
else:
form = InternApplicationForm()
return render_to_response('application.html', {'form': form},
context_instance = RequestContext(request))
The form is a ModelForm, and the underlying model contains an IntegerField.
When I post a form with empty value, the validation message is displayed just fine.
When I post the form with a non-integer value, I get this:
KeyError at /
'invalid'
It kind of surprises me that the code seems to crash on is_valid() call, which I assumed is safe (i.e. should return False if there is a problem and not just crash). How do I fix this?
Stacktrace
Django Version: 1.3
Python Version: 2.6.5
File "/usr/local/lib/python2.6/dist-packages/Django-1.3-py2.6.egg/django/core/handlers/base.py" in get_response
111. response = callback(request, *callback_args, **callback_kwargs)
File "/home/dan/www/ints/backend/views.py" in add_intern
14. if form.is_valid():
File "/usr/local/lib/python2.6/dist-packages/Django-1.3-py2.6.egg/django/forms/forms.py" in is_valid
121. return self.is_bound and not bool(self.errors)
File "/usr/local/lib/python2.6/dist-packages/Django-1.3-py2.6.egg/django/forms/forms.py" in _get_errors
112. self.full_clean()
File "/usr/local/lib/python2.6/dist-packages/Django-1.3-py2.6.egg/django/forms/forms.py" in full_clean
267. self._clean_fields()
File "/usr/local/lib/python2.6/dist-packages/Django-1.3-py2.6.egg/django/forms/forms.py" in _clean_fields
284. value = field.clean(value)
File "/usr/local/lib/python2.6/dist-packages/Django-1.3-py2.6.egg/django/forms/fields.py" in clean
169. value = self.to_python(value)
File "/usr/local/lib/python2.6/dist-packages/Django-1.3-py2.6.egg/django/forms/fields.py" in to_python
248. raise ValidationError(self.error_messages['invalid'])
Exception Type: KeyError at /
Exception Value: 'invalid'
Okay, so I just nailed it down.
I followed this advice to set my custom error message for validation.
So I had this code:
def __init__(self, *args, **kwargs):
super(InternApplicationForm, self).__init__(*args, **kwargs)
for field in self.fields.values():
field.error_messages = {'required':'*'}
that set the same required field validation message for all fields.
When the error was different (invalid for non-integer), Django looked in the dictionary I supplied—and guess what, KeyError. Because there is no message for invalid there (and that's my fault).
So the fix is
field.error_messages = {'required': '*', 'invalid': "That's not a number, sir."}
(and possibly other error message keys)

Categories

Resources