I get a MultiValueDictKeyError in Django 1.6 when I define a fieldset or field in my NestedStackedInline. There I have a custom ID as CharField and wanted to hide that field. But when I do that by exclude=('id',), defining fields without that or just making it readonly, I get a MultiValueDictKeyError.
Here my code:
class MaterialInline(NestedStackedInline):
fieldsets = ('name', 'amount', 'date')
#without custom id I get that error(id=models.Charfield(primary_key=True))
model = Material
extra = 1
form = autocomplete_light.modelform_factory(Material)
class ResearchAdmin(NestedModelAdmin):
inlines = [MaterialInline, ]
fields =('id','subject', 'topic')
Error:
Exception Type: MultiValueDictKeyError
Exception Value: "u'material_set-0-id'"
Exception Location: /usr/local/lib/python2.7/dist-packages/django/utils/datastructures.py in __getitem__, line 301
...
/home/administrator/src/django-nested-inlines/nested_inlines/admin.py in change_view
269. self.add_nested_inline_formsets(request, inline, formset)
/home/administrator/src/django-nested-inlines/nested_inlines/admin.py in add_nested_inline_formsets
65. for form in formset.forms:
Does anyone knows that problem?
Thanks in advance!
I had the exact same problem. Turned out that I just needed to make the field not editable in the model. Note the last argument here:
class Material(models.Model):
uuid = models.CharField(primary_key=True, max_length=128, editable=False)
Try to set a HiddenInput widget on the id field. So, define a model form for the admin:
class MaterialForm(forms.ModelForm):
id = forms.CharField(max_length=30, widget=HiddenInput, required=False)
class Meta:
model = Material
Then replace
form = autocomplete_light.modelform_factory(Material)
in MaterialInline with
form = MaterialForm
Clearly a bit more work would be required to get it working with the autocomplete.
Related
Looking for info how django formsets validation works, though it is more complicated than it sounds. I have a formset with values, part of these values can be inserted there by javascript (it means they do not exist in database yet).
class RequireOneFormSet(BaseInlineFormSet):
def clean(self):
if any(self.errors):
return
form_count = len([f for f in self.forms if f.cleaned_data])
if form_count < 1:
raise ValidationError(_('At least one %(object)s is required.') %
{'object':
_(self.model._meta.object_name.lower())})
class VariantInline(admin.StackedInline):
model = Variant
extra = 1
formset = RequireOneFormSet
class ProductAdmin(admin.ModelAdmin):
class Meta:
model = Product
class Media:
js = (os.path.join(STATIC_URL, 'js', 'admin_utils.js'), )
exclude = ('slug',)
filter_horizontal = ('category',)
inlines = [ImageInline, DetailInline, VariantInline]
manufacturer = ModelChoiceField(Manufacturer.objects.all())
list_filter = ('name', 'manufacturer', 'category')
list_display = ('name', 'manufacturer')
search_fields = ('name',)
save_as = True
Next, basing on those entries I`d like to create objects during formset validation. Django complains that there is no such object in DB when 'Save' button is clicked.
I have tried to override clean method of model, clean of ModelAdmin, save_formset of formset but with no luck as these values created by javascript are filtered out earlier in process. I am looking for info which method takes care of that, and can it be overriden?
EDIT:
Added some code, used view is a generic one from Django.
I`ve managed to resolve it. Key was to create my own field and override clean() method there. As you can see in file django/forms/models.py in class ModelMultipleChoiceField clean() is responsible for checking send values.
class DetailsField(ModelMultipleChoiceField):
def clean(self, value):
(code here)
class VariantForm(ModelForm):
details = DetailsField(queryset=Detail.objects.all(),
widget=FilteredSelectMultiple('details', False))
class VariantInline(admin.StackedInline):
model = Variant
extra = 1
formset = RequireOneFormSet
form = VariantForm
I have to generate a FormSet from a model but I need to insert an "extra value" in to every form.
Specifically, I have a JApplet that generates some Markers and Paths on a image, and POST it on the server.
In my model lines are composed from two Markers. But when I POST it, because I'm using the id generated from the JApplet and not from the database, I will not know from which Markers a Path will be composed.
So I thought to insert a "temporary id" on the Marker on the form, and do the correct arrangements in the view before saving the Path.
I thought about defining a custom form for the markers, but it not seems to be very DRY, and I don't want to came back to this if I change the Marker model.
Here is the form:
class PointForm(forms.ModelForm):
temp_id = forms.IntegerField()
class Meta:
model = Point
def clean(self):
if any(self.errors):
# Don't bother validating the formset unless each form is valid on its own
return
ingresso = self.cleaned_data['ingresso']
ascensore = self.cleaned_data['ascensore']
scala = self.cleaned_data['scala']
if (ingresso and ascensore) or (ingresso and scala) or (ascensore and scala):
raise forms.ValidationError("A stair cannot be a elevator or an access!!!")
return self
def save(commit=True):
# do something with self.cleaned_data['temp_id']
super(PointForm).save(commit=commit)
And the model:
class Point(models.Model):
RFID = models.CharField(max_length=200, blank=True)
x = models.IntegerField()
y = models.IntegerField()
piano = models.ForeignKey(Floor)
ingresso = models.BooleanField()
The error:
ViewDoesNotExist at /admin/
Could not import buildings.views.getFloors. View does not exist in module buildings.views.
Request Method: GET
Request URL: http://127.0.0.1:8000/admin/
Django Version: 1.4.1
Exception Type: ViewDoesNotExist
Exception Value:
Could not import buildings.views.getFloors. View does not exist in module buildings.views.
Exception Location: /usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py in get_callable, line 101
The error is generated when I try to load the admin page, this page has no references at all with the form.
SOLUTION FOR EXCEPTION
Ok, I'll write here how to find out why Django was doing such a strange thing.
Here it's a correct way to find out what is the problem.
The exception was thrown because I forgot to add forms.py to the from django import forms.
You can add a field to a ModelForm. Unless you add a field named temp_id to your model you do not need to change this form when you change you model.
Example (with a model named Point):
class PointForm (forms.ModelForm):
temp_id = forms.IntegerField()
class Meta:
model = Point
def save(self, commit=True):
# do something with self.cleaned_data['temp_id']
return super(PointForm, self).save(commit=commit)
UPDATE: Forgot self in def save() and changed modelname to Point
To follow up relekang's answer, I had to be reminded to also return the last line as shown, so that the object's get_absolute_url() method could be automatically called on submission of the form:
return super(PointForm, self).save(commit=commit)
I'm getting a "DoesNotExist" error with the following set up - I've been trying to debug for a while and just can't figure it out.
class Video(models.Model):
name = models.CharField(max_length=100)
type = models.CharField(max_length=100)
owner = models.ForeignKey(User, related_name='videos')
...
#Related m2m fields
....
class VideoForm(modelForm):
class Meta:
model = Video
fields = ('name', 'type')
class VideoCreate(CreateView):
template_name = 'video_form.html'
form_class = VideoForm
model = Video
When I do this and post data for 'name' and 'type' - I get a "DoesNotExist" error. It seems to work fine with an UpdateView - or when an 'instance' is passed to init the form.
This is the exact location where the error is raised:
/usr/lib/pymodules/python2.7/django/db/models/fields/related.py in get, line 301
Does anyone know what might be going on?
Thanks
Since you have not posted your full traceback, my guess is that your owner FK is not optional, and you are not specifying one in your model form.
You need to post a full traceback.
I think it has to be class VideoForm(ModelForm) instead of VideoForm(modelForm).
If you aren't going to use the foreign key in the form use exclude = ('owner')
Even though a field is marked as 'editable=False' in the model, I would like the admin page to display it. Currently it hides the field altogether.. How can this be achieved ?
Use Readonly Fields. Like so (for django >= 1.2):
class MyModelAdmin(admin.ModelAdmin):
readonly_fields=('first',)
Update
This solution is useful if you want to keep the field editable in Admin but non-editable everywhere else. If you want to keep the field non-editable throughout then #Till Backhaus' answer is the better option.
Original Answer
One way to do this would be to use a custom ModelForm in admin. This form can override the required field to make it editable. Thereby you retain editable=False everywhere else but Admin. For e.g. (tested with Django 1.2.3)
# models.py
class FooModel(models.Model):
first = models.CharField(max_length = 255, editable = False)
second = models.CharField(max_length = 255)
def __unicode__(self):
return "{0} {1}".format(self.first, self.second)
# admin.py
class CustomFooForm(forms.ModelForm):
first = forms.CharField()
class Meta:
model = FooModel
fields = ('second',)
class FooAdmin(admin.ModelAdmin):
form = CustomFooForm
admin.site.register(FooModel, FooAdmin)
Add the fields you want to display on your admin page.
Then add the fields you want to be read-only.
Your read-only fields must be in fields as well.
class MyModelAdmin(admin.ModelAdmin):
fields = ['title', 'author', 'published_date', 'updated_date', 'created_date']
readonly_fields = ('updated_date', 'created_date')
You could also set the readonly fields as editable=False in the model (django doc reference for editable here). And then in the Admin overriding the get_readonly_fields method.
# models.py
class MyModel(models.Model):
first = models.CharField(max_length=255, editable=False)
# admin.py
class MyModelAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None):
return [f.name for f in obj._meta.fields if not f.editable]
With the above solution I was able to display hidden fields for several objects but got an exception when trying to add a new object.
So I enhanced it like follows:
class HiddenFieldsAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None):
try:
return [f.name for f in obj._meta.fields if not f.editable]
except:
# if a new object is to be created the try clause will fail due to missing _meta.fields
return ""
And in the corresponding admin.py file I just had to import the new class and add it whenever registering a new model class
from django.contrib import admin
from .models import Example, HiddenFieldsAdmin
admin.site.register(Example, HiddenFieldsAdmin)
Now I can use it on every class with non-editable fields and so far I saw no unwanted side effects.
You can try this
#admin.register(AgentLinks)
class AgentLinksAdmin(admin.ModelAdmin):
readonly_fields = ('link', )
I want to make a form used to filter searches without any field being required. For example given this code:
models.py:
class Message(models.Model):
happened = models.DateTimeField()
filename = models.CharField(max_length=512, blank=True, null=True)
message = models.TextField(blank=True, null=True)
dest = models.CharField(max_length=512, blank=True, null=True)
fromhost = models.ForeignKey(Hosts, related_name='to hosts', blank=True, null=True)
TYPE_CHOICES = ( (u'Info', u'Info'), (u'Error', u'Error'), (u'File', u'File'), (u'BPS', u'BPS'),)
type = models.CharField(max_length=7, choices=TYPE_CHOICES)
job = models.ForeignKey(Jobs)
views.py:
WHEN_CHOICES = ( (u'', ''), (1, u'Today'), (2, u'Two days'), (3, u'Three Days'), (7, u'Week'),(31, u'Month'),)
class MessageSearch(ModelForm): #Class that makes a form from a model that can be customized by placing info above the class Meta
message = forms.CharField(max_length=25, required=False)
job = forms.CharField(max_length=25, required=False)
happened = forms.CharField(max_length=14, widget=forms.Select(choices=WHEN_CHOICES), required=False)
class Meta:
model = Message
That's the code I have now. As you can see it makes a form based on a model. I redefined message in the form because I'm using an icontains filter so I didn't need a giant text box. I redefined the date mostly because I didn't want to have to mess around with dates (I hate working with dates! Who doesnt?) And I changed the jobs field because otherwise I was getting a drop down list of existing jobs and I really wanted to be able to search by common words. So I was able to mark all of those as not required
The problem is it's marking all my other fields as required because in the model they're not allowed to be blank.
Now in the model they can't be blank. If they're blank then the data is bad and I don't want it in the DB. However the form is only a filter form on a page to display the data. I'm never going to save from that form so I don't care if fields are blank or not. So is there an easy way to make all fields as required=false while still using the class Meta: model = Message format in the form? It's really handy that I can make a form directly from a model.
Also this is my first serious attempt at a django app so if something is absurdly wrong please be kind :)
You can create a custom ModelForm that suit your needs. This custom ModelForm will override the save method and set all fields to be non-required:
from django.forms import ModelForm
class SearchForm(ModelForm):
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
for key, field in self.fields.iteritems():
self.fields[key].required = False
So you could declare your forms by simply calling instead of the ModelForm, e.g.:
class MessageForm(SearchForm):
class Meta:
model = Message
You could also pass empty_permitted=True when you instantiate the form, e.g.,
form = MessageSearch(empty_permitted=True)
that way you can still have normal validation rules for when someone does enter data into the form.
I would give a try to the django-filter module :
http://django-filter.readthedocs.io/en/develop/
fields are not required. these are filters actually. It would look like this :
import django_filters
class MessageSearch(django_filters.FilterSet):
class Meta:
model = Message
fields = ['happened', 'filename', 'message', '...', ]
# django-filter has its own default widgets corresponding to the field
# type of the model, but you can tweak and subclass in a django way :
happened = django_filters.DateFromToRangeFilter()
mandatory, hidden filters can be defined if you want to narrow a list of model depending on something like user rights etc.
also : setup a filter on a 'reverse' relationship (the foreignkey is not in the filtered model : the model is referenced elsewhere in another table), is easy, just name the table where the foreign key of the filtered model field is :
# the 'tags' model has a fk like message = models.ForeignKey(Message...)
tags= django_filters.<some filter>(name='tags')
quick extendable and clean to setup.
please note I didn't wrote this module, I'm just very happy with it :)