I have a model for which the need is to show the form multiple times. I have used it under a modelformset. I seem to have a problem with the id of this model which is also the primary key for the model.
I prepopulate the formset with data which I wish to edit.
But whenever I click on submit it refreshes the page back with an error saying '(Hidden field id) with this None already exists.'
This error comes specifically for the 'id' field which is hidden
<input type="hidden" id="id_form-0-id" value="2972" name="form-0-id"/>
This is the snippet from the template. (I got it from firebug)
What could the issue possibly be since the form is invalid I am not able to save the data.
ProfilesFormSet = modelformset_factory(Profile,exclude = ( <items spearated by commas>), extra=0)
profile_form_set = ProfilesFormSet(queryset = Profile.objects.filter(userprofile=userprofile).order_by('-modified_on'))
This is the code snippet.
If you're using PostgreSQL and any version of Django prior to 1.1beta, and your model does not have a default ordering defined, I think you're probably seeing the bug related to inconsistent ordering of objects returned from the database (see Django Trac tickets 9076, 9758, 10163 among others).
Try setting a default ordering on the model:
class Meta:
ordering = ('some_field',)
See if that fixes it.
I believe this error is caused by one of the following:
The Django form object you are using inside the formset does not include the primary key (id) of the model. However, since you are using modelformset_factory this shouldn't be the case (you also wouldn't be getting that error message).
The HTML form in your template does not include the primary key, even as a hidden field. Make sure you have {{ form.id }} or something like that in your template, inside the {{ for form in formset }} loop.
I can't think of any more reasons at the moment, but I'm sure they are all going to be related to the form POST'ed back from the browser client is missing the id field somehow.
Related
I am developing an application which have related models like Trip can have multiple loads.
So how can I design the create form to achieve the desired functionality? I know formset can be used to achieve the functionality but I want custom functionality like:
User can create new load while creating trip, the added loads will be show in a html table within the form with edit and delete functionality being on the Trip create page.
I have achieved the functionality using two ways but I am looking for a neater and cleaner approach. What I have done so far was:
I added the Loads using ajax and retrieved the saved load's id and store in a hidden field, when submitting the main Trip's form i create object for Trip and retrieve Loads's object using id and map that Trip id in the Load's object.
I have keep Loads data in the javascript objects and allow user to perform add edit delete Loads without going to the database, once user submit the main Trip form i post Loads's data and create objects and save in the database.
Please let me know if anybody have done this type of work and have a cleaner approach
This sounds like a good use case for django-extra-views.
(install with pip install django-extra-views and add extra_views to your INSTALLED_APPS)
You can create and update models with inlines with simple class based views.
In Views
from extra_views import CreateWithInlinesView, UpdateWithInlinesView, InlineFormSetFactory
from .models import Load, Trip
class LoadInline(InlineFormsetFactory):
model = Load
fields = [<add your field names here>]
class CreateTripView(CreateWithInlinesView):
model = Trip
inlines = [LoadInline]
fields = [<add your trip model fields here>]
template = 'trips/create.html`
template:
<form method="post">
...
{{ form }}
{% for formset in inlines %}
{{ formset }}
{% endfor %}
...
<input type="submit" value="Submit" />
</form>
The update view is very similar. Here are the docs:
https://github.com/AndrewIngram/django-extra-views#createwithinlinesview-or-updatewithinlinesview
The thing that often gets overlooked is commit=False, and the ability to process more than one form or formset in a single view. So you could have a formset for trips, and a form for load, and process the information creating all the objects once all the forms validate.
Here is a restructured view function outline which I use to process multiple forms (haven't edited my app-specific names, don't want to introduce errors):
def receive_uncoated( request): #Function based view
# let's put form instantiation in one place not two, and reverse the usual test. This
# makes for a much nicer layout with actions not sandwiched by "boilerplate"
# note any([ ]) forces invocation of both .is_valid() methods
# so errors in second form get shown even in presence of errors in first
args = [request.POST, ] if request.method == "POST" else []
batchform = CreateUncWaferBatchForm( *args )
po_form = CreateUncWaferPOForm( *args, prefix='po')
if request.method != "POST" or any(
[ not batchform.is_valid(), not po_form.is_valid() ]):
return render(request, 'wafers/receive_uncoated.html', # can get this out of the way at the top
{'batchform': batchform,
'po_form': po_form,
})
#POST, everything is valid, do the work
# create and save some objects based on the validated forms ...
return redirect( 'wafers:ok' )
NB the use of any is vital. It avoids Python short-cut evaluation of a conditional and therefore makes all errors on second and subsequent forms available to the user even if the first form failed validation.
Back to this question: You would replace batch_form with a trip_form, and po_form with a ModelFormset for loads. After your form and formset both validated, you would create all the requested objects using
trip = trip_form.save( commit=False)
load_list = loads_formset.save( commit = False)
# fill in some related object in trip then save trip
foo = Foo.objects.get( ...)
trip.foo = foo
trip.save()
# link loads to trip and save them
for load in load_list:
load.trip = trip
load.save()
You probably want to run this in a transaction so that if something goes wrong (DatabaseError) with saving one of the loads, you don't get a partial trip stored in the database but rather get the whole thing rolled back.
My django model by default has an auto generated id field as primary key which is named 'id' by default on my db.
As such my model has a lot of other fields that I am selectively displaying using a Django model form. While doing so I specify 'id' for this auto generated id field but my form does not display or generate this field on HTML at all even though it seems to recognize this as a valid field.
I have also checked by doing an 'all' for fields but this is also not bringing in that specific field. The funny thing is django does seem to recognize this field as 'id' because if I change it to 'Id' or anything else random it immediately gives me an error for example "Unknown field(s) (Id) specified for....." on my forms.py
This 'id' is important as I am using it to link and retrieve related objects using Ajax on client side. If this does not work then I will be forced to use another field as primary key. Any suggestions?
I ended up using a workaround as suggested by 'mastazi' for passing this id as a hidden field.
All I did is access this hidden field and generate it as an html element. Since I am using crispy_forms layout I was able to use an HTML element under Layout.
form_id= self.instance.id
id_hidden = '<input type="hidden" id="form_id" value=' + str(form_id) +' />'
I am not sure if this is a perfect solution but it works.
I need to make a model field read only in my form. I do this with the following:
class ActivityEntryForm(forms.ModelForm):
activity = forms.CharField(widget=forms.TextInput(attrs={'readonly':'readonly'}))
duration = forms.IntegerField()
class Meta:
model = CustomerActivity
fields = ('activity', 'duration',)
Activity is an actual model foreign key field to the CustomerActivity model.
This currently displays the Activity Id number. I want it to print the name of the activity.
What field type do I use or how can I show the name instead of the id?
First of all, note that the readonly attribute is not safe to prevent changes. Any nitwit can change the html code nowadays with inspect element etc. built into any browser. For security reasons it's better to exclude the field from the form and present the data in another way, or to have a server-side check to prevent people from changing data.
That's also a good way to solve your question. As an instance of a ModelForm, your form has an instance attribute which holds the actual data related to the object, given that this data exists. Instead of going through an unused form field, use the data form the instance as-is:
<input type="text" readonly="readonly">{% firstof form.instance.activity.name "<default>" %}</input>
I'm building a "preview" function into the CMS for my website which uses the existing front-end template to render a model. This model has an association:
class FeatureWork(models.Model):
name = models.CharField(max_length=100)
...
class FeatureWorkLink(models.Model):
feature_work = models.ForeignKey(FeatureWork)
In the view for the preview, I'm trying to build the model such that when the template calls feature.featureworklink_set.all it returns the associated links. Since neither model has been saved yet, all the standard Django form techniques seem to go out the window.
This is what I have so far, but it blows up when I call the add method on the manager, since the parent hasn't been saved yet:
form = FeatureWorkAdminForm(initial=request.POST)
featured = form.save(commit=False)
for link in request.POST['links'].split(","):
featured.featureworklink_set.add(FeatureWorkLink(image=link))
Why don't you just add:
preview = BooleanField()
to your models, save everything to database and don't look for hacks. This way you can have drafts for free.
You can actually save it in transaction and rollback when template is ready. It's not very efficient, but at least it will work.
featured.featureworklink_set.add(FeatureWorkLink(image=link)) will immediately attempt to create a relationship between a FeatureWork and FeatureWorkLink, which isn't going to happen because that instance of FeatureWork is not present in the database and you cannot satisfy the predicates for building the foreign key relationship.
But the great thing is that Django's Model and ModelForm instances will not validate foreign key relationships until your actually trying to commit the data. So manually constructing your FeaturedWorkLink with an uncommited, non-existing FeatureWork should satisfy any presentation of the data you need to do, much to what you'd expect:
links = []
form = FeatureWorkAdminForm(initial=request.POST)
featured = form.save(commit=False)
for link in request.POST['links'].split(","):
links.add(FeatureWorkLink(image=link, feature_work=featured))
# then somewhere in your templates, from the context
{% for link in links %}
<img src="{{ link.image }}"
title="Image for the featured work: '{{ link.feature_work.name }}'" />
{% endfor %}
So basically, during the course of collecting the data to create a FeatureWork, you'll have to maintain the FeatureWorkLink instances through subsequent requests. This is where you'd use a model form set, but provide an uncommitted FeatureWork for the feature_work property for every model form instance of the set, up until the point where all the data has been collected, where you then provide a commited FeatureWork instance, so that the model form set can satisfy referential integrity and finally be commited to the database.
A while back I made a Model class. I made several ModelForms for it and it worked beautifully.
I recently had to add another optional (blank=True, null=True) field to it so we can store some relationship data between Users. It's essentially a referral system.
The problem is adding this new field has meant the referral field shows up where I haven't changed the ModelForms to exclude it. Normally this would just mean an extra 10 minutes going through and excluding them but in this case, due to project management politics out of my control, I only have control over the Models for this application.
Can I either:
Set the field to auto-exclude?
Set it so it renders as a hidden (acceptable if not perfect)?
If you have access to the template you could render it as a hidden field with the following code:
{{ form.field_name.as_hidden }}
instead of the standard:
{{ form.field_name }}
from the docs on Using a subset of fields on the form:
Set editable=False on the model field. As a result, any form created from the model via ModelForm will not include that field.
You could define a custom model field subclass and override the formfield() method to return a field with a HiddenInput widget. See the documentation for custom fields.
Though you mentioned that you cannot use exclusion in your case, I think others who come across this answer (like myself, based on the title) may find it helpful.
It is possible to selectively hide fields using exclude in ModelAdmin, here is a snippet from something I'm working on:
class ItemsAdmin(admin.ModelAdmin):
form = ItemsForm
actions = None
list_display = ('item_id', 'item_type', 'item_title', 'item_size', 'item_color',)
search_fields = ('item_id', 'item_title',)
inlines = [ImageInline,]
readonly_fields = ('disable_add_date','disable_remove_date',)
exclude = ('add_date', 'remove_date',)
###.............