Get newly added forms from django formset - python

I have a formset as follows:
TableAddFormSet = modelformset_factory(Table, form=TableAddForm)
The model looks like this:
class Table(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
amount_of_people = models.IntegerField()
category = models.CharField(max_length=10)
reserved = models.BooleanField(default=False)
Now the model required the attribute 'restaurant', which I will set on form-submission. Until now I've done the following:
for form in formset:
form.instance.restaurant = request.user.restaurant
which means that even forms that already existed get looped through and updated. Is there a more efficient way to add this attribute to the newly added forms, something like:
for form in formset.new_forms():
or is my implementation the most suitable way for solving this problem?

You should be able to use inlineformset_factory like this:
TableAddFormSet = inlineformset_factory(Restaurant, Table, form=TableAddForm)
table_formset = TableAddFormSet(request.POST or None, instance=request.user.restaurant)
As the name implies, it's more intended for when you create a form that has a formset within it (so, a "restaurant" form that has multiple "table" entries within it), but it should work fine for what you're doing too.

Related

How can I concatenate two different model query and order by a field that both models has

How can I concatenate two different model query and order by a field that both models has like progress fields.
For example
models.py
class Gig(models.Model):
author= models.ForeignKey(User)
title = models.CharFields()
progress = models.IntegerField()
class Project(models.Model):
author= models.ForeignKey(User)
title = models.CharFields()
progress = models.IntegerField()
Can I do my view.py like this, for me to achieve it?
İf No, How can I achieve it?
views.py
def fetch_all_item(request):
gig = Gig.objects.filter(author_id = request.user.id)
project = Project.objects.filter(author_id = request.user.id)
total_item = (gig + project).order_by("progress")
return render(request, "all_product.html", {"item": total_item})
I am trying to join two query set from Gig and Project models then send it to frontend in an ordering form by a field name called progress.
You can let Python do the sorting for you, like:
from operator import attrgetter
def fetch_all_item(request):
gig = Gig.objects.filter(author=request.user)
project = Project.objects.filter(author=request.user)
total_item = sorted([*gig, *project], attrgetter('progress'))
return render(request, "all_product.html", {'item': total_item})
It might however be better to remodel this to a single model with a type field that disambiguates between a Gig and a Project.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
In general, such design when there are common fields is better accomplished by inheriting from some base class. E.g.:
class CommonActivity(models.Model):
# common fields for Project/Gig
author = models.ForeignKey(User)
title = models.CharFields()
progress = models.IntegerField()
class Gig(CommonActivity):
pass # or other fields related to Gig only
class Project(CommonActivity):
pass
Then if you want to query both - you query CommonActivity.
If remodel not possible - then filter via Python as #WillemVanOnsem suggested.
However such filtering will be way less efficient

Extending Django User model - form population errors

I'm extending Django's (v1.9) built-in User model with Player class, to add some extra properties.
class Player(models.Model):
TIMEZONES=()
user = models.OneToOneField(User, on_delete=models.CASCADE)
... (player-specific properties here)
time_zone = models.CharField(max_length=255, choices=PRETTY_TIMEZONE_CHOICES, blank=True, null=True,)
When creating users from Django admin panel, I don't always need to create players, so sometimes only User gets created. As a result, Player and User IDs don't exactly match. Turns out that this leads to a problem when populating ModelForms of models that are linked to Player, like this one:
class City(models.Model):
player = models.ForeignKey(Player, on_delete=models.CASCADE)
name = models.CharField(max_length = 100)
x_coord = models.SmallIntegerField()
y_coord = models.SmallIntegerField()
region = models.CharField(max_length = 100)
def __unicode__(self):
return str(self.player) + "-" + str(self.name)
class Meta:
db_table = 'cities'
class CityForm(ModelForm):
class Meta:
model = City
fields = (
'name',
'player',
'x_coord',
'y_coord',
'region')
This modelForm is used when creating a new city. When User ID and Player ID match, there is no problem, player ID gets populated in the form and city is successfully created. When User ID and Player ID are different, player ID is not populated in the form, the form fails to validate, and city creation fails.
I have no problem getting Player ID from request.user, and I could fix up the player ID before validating after getting POST data. I've also added a post-save hook so that Player always gets created, so the IDs will always match. But it seems that form should be populated with player ID in the first place, since user data is accessible and it's a one to one relationship.
What am I missing here?
What you are missing is that when you instantiate a ModelForm to create a new row that's related to some existing object, Django has no way of knowing the id of the related object. You need to tell it somehow.
One way to do that is, when you are displaying the form in response to a GET, use the initial argument to the form constructor:
myform = MyModelFormClass(None, initial={ 'myfkfield': myrelatedobject.pk })
Now the form class knows what value to pre-fill in when it renders the form, and when the form is posted, that field will be posted with it.
The other way to do it would be to omit the relation field from your form altogether, then fill it in later before you save, by using the commit argument to the form save method:
myform = MyModelFormClass(request.POST)
# this causes form values to be filled into the instance without actually
# writing to the database yet.
myinstance = myform.save(commit=False)
myinstance.myfkfield = myrelatedobject
# now really write to database
myinstance.save()
Note that this would be for an insert. For updates, you need to supply the existing object to your modelform constructor, like this:
myinstance = MyModel.objects.get(pk=self.kwargs.pk)
myform = MyModelFormClass(request.POST, instance=myinstance)
Without the instance, ModelForm doesn't know what row it's updating in the database. You would think that this is all present in the HTML so it shouldn't be necessary.. but that's not how Django works. You need to fetch the object existing from the database, and pass it to the ModelForm constructor along with the request.POST data. Then when you call myform.save() it will validate the form, merge its data with the existing object, and save the object. Using commit=False results in the last of those three steps being deferred, so that you can make any adjustments or checks to the updated instance before it is actually saved.

Django modelform - when errors in the form it is showing excluded fields and losing translations?

Im seeing a weird behaviour with my modelform. If I submit and a field has errors, when I re display it with the errors, excluded fields are appearing. Also its happening that the fields were translated into spanish and I m getting the labels in the original English.
I m using django 1.6.10, does anyone know if this is a bug or I m doing something wrong?
I'm not doing anything fancy with the rendering, I'm just doing {{ form }} in the template.
For instance this is a normal form
But if I submit the field empty I get the extra excluded field "notes" and the original field's name in English "plate number":
Here is the model code:
class Operation3Domain(models.Model):
operation = models.ForeignKey(Operation, unique=True)
date = models.DateTimeField(auto_now_add=True)
update_date = models.DateTimeField(auto_now=True)
notes = models.TextField(blank=True, null=True)
#this one is the only one I want to show:
plate_number = NumberPlateField()
class Meta:
app_label = "operation"
and the model form:
class Operation3DomainForm(forms.ModelForm):
class Meta:
model = Operation3Domain
labels = { 'plate_number': 'Dominio/Patente'}
exclude = ['operation', 'notes']
I appreciate any help! Thanks!!
Let me answer my own question!
So the problem to excluded fields was that in the creation of the form I had an outdated exclude parameter (sorry!), which is overriding the form's exclude.
form_class = modelform_factory(step_model, exclude=('operation',))
form = form_class(request.POST, instance=instance)
BUT! I still don't know why the form appears in English instead of spanish, which doesn't happen if I use the form class directly:
form = Operation3DomainForm(request.POST, instance=instance)
Thanks!

creating django forms

I'm struggling to get my head round django forms.. I've been reading various documentation but just can't quite grasp the concepts. I have got to grips with models, views and templates. What I am trying to do is to create a form with various fields composing of dropdown lists and checkboxes which are populated by values in a database.
I have a working app called vms. Using the models.py I have a built a simple schema that holds size and type. Size consists of 'small', 'medium' & 'large'. Type is 'windows' & 'linux'. Using the admin site, I can add an extra size, for example 'Extra Large'.
What I would like to do is create a form that has a drop down list of the vm sizes. If an extra size gets added via the admin site, I would like that size to appear in the drop down list.
I would submit my attempts at the code, but actually am struggling with the concepts. Can anyone help guide me in how to accomplish the above?
Thanks
Oli
Forms are just a tool to simplify and speed-up (the development of) the process of fetching POST data from the request. A manual way would be to do request.POST.get('somefield') for all the fields there are in some HTML form. But Django can do better than that...
In its essence, a Form class holds a number of Fields and performs these tasks:
display HTML inputs,
collect and validate data when user submits it,
if fields don't validate, return the values along with error messages to HTML,
if all fields validate, provide form.cleaned_data dictionary as a convenient way to access these values in view.
With these values, I could then manually create a new instance of a MyModel and save it. Of course, I would have to define a Field in the Form for every Field in MyModel model.
This means that, basically, I could do something like this:
(forgive me for not testing this code, so I can't vouch that it's 100% correct)
models.py:
class MyModel(models.Model):
field1 = models.CharField(max_length=40, blank=False, null=False)
field2 = models.CharField(max_length=60, blank=True, null=True)
forms.py:
class FormForMyModel(forms.Form):
form_field1 = forms.CharField(max_length=40, required=True)
form_field2 = forms.CharField(max_length=60, required=False)
views.py:
def create_a_my_model(request):
if request.method == 'POST':
form = FormForMyModel(request.POST)
if form.is_valid():
my_model = MyModel()
my_model.field1 = form.cleaned_data.get('form_field1', 'default1')
my_model.field2 = form.cleaned_data.get('form_field2', 'default2')
my_model.save()
else:
form = FormForMyModel()
context_data = {'form': form}
return HttpResponse('templtate.html', context_data)
(this could be written with a few lines of code less, but it's meant to be as clear as possible)
Notice there are no relation between model Fields and form Fields! We have to manually assign values to MyModel instance when creating it.
The above example outlines generic form workflow. It is often needed in complex situations, but not in such a simple one as is this example.
For this example (and a LOT of real-world examples), Django can do better than that...
You can notice two annoying issues in the above example:
I have to define Fields on MyModel and Fields on FormForMyModel separately. However, there is a lot of similarity between those two groups (types) of Fields, so that's kind of duplicate work. The similarity grows when adding labels, validators, etc.
creating of MyModel instance is a bit silly, having to assign all those values manually.
This is where a ModelForm comes in.
These act basically just like a regular form (actually, they are extended from regular forms), but they can save me some of the work (the two issues I just outlined, of course :) ).
So back to the two issues:
Instead of defining a form Field for each model Field, I simply define model = MyModel in the the Meta class. This instructs the Form to automatically generate form Fields from model Fields.
Model forms have save method available. This can be used to create instance of model in one line in the view, instead of manually assigning field-by-field.
So, lets make the example above with a ModelForm:
models.py:
class MyModel(models.Model):
field1 = models.CharField(max_length=40, blank=False, null=False)
field2 = models.CharField(max_length=60, blank=True, null=True)
forms.py:
class MyModelForm(forms.ModelForm): # extending ModelForm, not Form as before
class Meta:
model = MyModel
views.py:
def create_a_my_model(request):
if request.method == 'POST':
form = MyModelForm(request.POST)
if form.is_valid():
# save the model to database, directly from the form:
my_model = form.save() # reference to my_model is often not needed at all, a simple form.save() is ok
# alternatively:
# my_model = form.save(commit=False) # create model, but don't save to database
# my.model.something = whatever # if I need to do something before saving it
# my.model.save()
else:
form = MyModelForm()
context_data = {'form': form}
return HttpResponse('templtate.html', context_data)
Hope this clears up the usage of Django forms a bit.
Just one more note - it is perfectly ok to define form Fields on a ModelForm. These will not be used in form.save() but can still be access with form.cleaned_data just as in a regular Form.
Have you tried working with ModelForms before? As I understand, you're looking to create a form based on the model you created right?
Lets say your model is called Temp. You can create a form that correlates with this model (and your question) like this:
forms.py
from django.forms import ModelForm
class TempForm(ModelForm):
class Meta:
model = Temp
The ModelForm will automatically map the selections/choices from your model to a form version.
If you plan on using this in a template later, doing something like this will automatically create a drop-down menu with choices:
<form>
<label for="id_size">Size</label>
{{ form.size }}
</form>
Hope that answers your question!
Simply use CharField in your modelform as below:
SIZES_CHOICES = (
('size1', 'M'),
('size2', 'L'),
)
size = models.CharField(max_length=100, choices=SIZES_CHOICES, default=size1)
in the above code, size1 is the value which will be going to store in your database as name 'size1' and in the drop-down menu, there will be an option is 'M' of right side.you can mentioned any name to these options.

Implementing USZipCodeField and USStateField in django

I'm looking to implement a zipcode field in django using the form objects from localflavor, but not quite getting them to work. I want to have a zipcode field in a form (or ModelForm in my case), but the fields never validate as a zipcode when calling _get_errors() on the form object. The way I'm implementing it seems right to me but is apparently wrong, does anyone know what the right way to do this might be?
I have a ModelForm that I want to use zipcode (and also USStateField) in:
from django.contrib.localflavor.us.forms import USStateField
from django.contrib.localflavor.us.forms import USZipCodeField
class FooForm(ModelForm):
class Meta:
model = Bar
fields = ('address', #This form uses a subset of fields from the model
'address_apt',
'address_city',
'address_state',
'address_zip',
'home_phone',
'mobile_phone')
widgets= {
'address_zip' : USZipCodeField(),
'address_state' : USStateField(),
}
The ModelForm 'FooForm' links to a model that looks like:
from django.contrib.localflavor.us import models as usmodels
class Bar(models.Model):
db_table = 'BAR'
address = models.CharField(max_length=255)
address_apt = models.CharField(max_length=40, blank=True)
address_city = models.CharField(max_length=90)
address_state = usmodels.USStateField()
address_zip = models.CharField(max_length=15)
home_phone = usmodels.PhoneNumberField( )
mobile_phone = usmodels.PhoneNumberField( )
#... There are more fields in the model...
But if I create an instance of the form and run it's validation, it never cares about the form level validation, only the model level validation:
foo_instance = FooForm(request.POST)
#Let's assume request.POST looks like:
#<QueryDict: {u'address_city': [u'asdf'], u'mobile_phone': [u'asdf'], u'address_state': [u'California'], u'home_phone': [u'asdf'], [u'1'], u'address': [u'123 foo'], u'address_zip': [u'asdf']}>
foo_instance._get_errors()
Yields:
<ul class="errorlist">
<li>mobile_phone<ul class="errorlist">
<li>Phone numbers must be in XXX-XXX-XXXX format.</li></ul>
</li><li>home_phone<ul class="errorlist">
<li>Phone numbers must be in XXX-XXX-XXXX format.</li></ul>
</li></ul>
I need to be able to call validation on the populated form object and have it tell me that the zipcode is formated improperly if so. Doing something wrong, just don't know what atm.
Using widgets declaratively has literally only just been added to the trunk SVN version in the last day or so. If you're using an older checkout, or a released version, it won't work - you'll need to go back to the old way of doing it, by overriding the field declarations at the top level of the form.

Categories