default value for ChoiceField in modelform - django

I have problem with django:
models.py:
SUSPEND_TIME = (
('0', '0'),
('10', '10'),
('15', '15'),
('20', '20'),
class Order(models.Model):
user = models.ForeignKey(User)
city = models.CharField(max_length=20)
...
processed = models.BooleanField(default=False)
suspend_time = models.CharField(max_length=2, choices=SUSPEND_TIME, default='0')
..
form.py:
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ('suspend_time', 'processed')
view.py:
try:
order = Order.objects.get(id=order_id)
except Order.DoesNotExist:
order = None
else:
form = OrderForm(request.POST, instance=order)
if form.is_valid():
form.save()
....
then I send ajax request to update instance with only "processed" param..
form.is_valid is always False if I don't send "suspend_time" !
if request contain {'suspend_time': 'some_value' ...} form.is_valid is True
I don't understand why ? suspend_time has default value.. and order.suspend_time always has some value: default or other from choices.
why after form = OrderForm(request.POST, instance=order) form['suspend_time'].value() is None, other fields (city, processed) has normal value .

The behavior is as expected. The form should validate with given data. i.e. Whatever required fields are defined in the form, should be present in the data dictionary to instantiate it.
It will not use data from instance to populate fields that are not provided in form data.
Text from django model forms
If you’re building a database-driven app, chances are you’ll have forms that map closely to Django models. For instance, you might have a BlogComment model, and you want to create a form that lets people submit comments. In this case, it would be redundant to define the field types in your form, because you’ve already defined the fields in your model.
For this reason, Django provides a helper class that let you create a Form class from a Django model.

Related

Fill Foreign Key Field in Form Field with Related Record in Django

I have 2 models Office & Meeting.
class Office(models.Model):
name = models.CharField(verbose_name=u"name",max_length=255)
class Meeting(models.Model):
meeting_office = models.ForeignKey(Registration,verbose_name=u"Office", on_delete=models.DO_NOTHING, related_name='meeting_office')
date = models.DateField(verbose_name=u"Date", null=False, blank=False)
I have a form that creates the blank meeting successfully
class MeetingForm(ModelForm):
class Meta:
model = Meeting
fields = (
'date',
'meeting_office'
)
widgets = {
'date' :forms.DateInput(attrs={'type': 'date'}),
'meeting_office' :forms.Select(attrs={'class': 'form-control'}),
When I want to have a prefilled form, i have a view that is below
def office_add_meeting(request, office_id):
print("office_id"+ office_id) # produces correct foreign key
office = Office.objects.get(pk=office_id)
form = MeetingForm(request.POST or None)
if form.is_valid():
form.instance.meeting_office = office
form.save()
messages.success(request, "Insert Successfull")
return HttpResponseRedirect('/office_main')
return render(request,
'Office/meeting-form.html',
{"form": form,
"office_id": office_id})
But the form does not prefill the foreign key field. Confirmed the office_id has been passed to the view successfully. Idon't see why the form is not using the defined instance. Any ideas what could be causing this? Or is there a better way?
To set the initial fields on a form, you can use a dictionary matching to the attribute names of the fields.
Try the following:
form = MeetingForm(initial={"meeting_office":your_office})
For editing existing forms, you can also use:
my_meeting = Meeting.objects.all().first() # Example of model
MeetingForm(instance=my_meeting) # Also use instance where you're accepting the form.

Django - modelform + model property

I am trying to solve one issue about saving data in db.
This is an example how I think of it:
class MyModel(models.Model):
id = models.AutoField(primary_key=True)
fieldX = models.SomeFieldType()
#property:
def foo(self):
return self._foo
#foo.setter
def foo(self, var):
self._foo=var
class MyModelForm(models.Modelform):
class Meta:
model = models.MyModel
fields = '__all__'
The thing is I have dict that I am passing to this form (so I am not using view or any provided interaction with user directly. In dict I have some fields and what I want to do is one field that is passed to use it as model property but I do not want it to be saved in db.
So I tried something like:
form = MyModelForm(data_dict)
if form.is_valid():
form.foo = data_dict['data_for_property_not_db']
form.save()
Form does not know that my model has this property.
So basiclly what I want is to write some parts of my data_dict normaly to form and db as always ->works fine
and then I want some data_info pass to that property and use it somewhere in save() as needed without saving it to db itself.
Can you help me with this?

Django - ModelForm display the item but insert the foreignkey

I have two models as ProjectContacts and the User:
class ProjectContacts(models.Model):
project_contact_fname = models.CharField(max_length=120, blank=False)
project_contact_lname = models.CharField(max_length=120, blank=False)
project_contact_group = models.ForeignKey(ProjectGroup)
project_contact_title = models.ForeignKey(ProjectContactTitles)
project_site_admin = models.BooleanField(default=False)
project_contact_add_date = models.DateTimeField(auto_now=True)
project_contact_defined_by = models.ForeignKey(User)
In my form I am presenting the project_contact_defined_by field in the form as a read-only TextInput() in the form.py so that I can show the full name of the currently logged-in user in the form as readonly. However, in my view I have to set the initial field in order to show the full name. Below is my view.py
def add_project_contact(request):
..............
form = ProjectContactForm(initial={'project_contact_defined_by' : request.user.get_full_name(),
}, )
return render(request,'members/rfc/create.html',{'form': form})
Now, I am having and issue with the record insertion here and I think when the form data gets inserted it is the full name that gets inserted not the user.id? I would appreciate it if someone can help me out here to resolve this.
Rather than displaying the user as read only form field, it would probably be better to exclude the field from your form, and display the user's name in the template, wherever you wish, as follows: {{ request.user.get_full_name }}
Then, after you check that the form is valid, you would set the User as follows:
project_contact = form.save(commit=False) # This initializes the object with form data without saving, so you can edit or alter fields before the model is created
project_contact.project_contact_defined_by = request.user
project_contact.save()
You would also want to check that the user exists (and is not anonymous):
if user.is_authenticated():
# Check that form is valid, then create model
Note that if you are using a generic FormView, then you will be overriding form_valid() (which already checks that the form is valid) and use self.request.user to access the user.

Django REST Framework: default fields in browseable API form

I have a model:
class XCall(models.Model):
created_on = models.DateTimeField(auto_now_add=True)
send_on = models.DateTimeField(default=datetime.now)
recipient = models.ForeignKey(User)
text = models.CharField(max_length=4096)
backup_calls = models.IntegerField(blank=True, null=True)
And a serializer for that model:
class CallSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='call-detail',
)
# some validation and custom field definitions
...
class Meta:
model = XCall
fields = ('url', 'id', 'text', 'recipient', 'send_on', 'backup_calls', 'status')
lookup_field= 'pk'
And here's the list view:
class CallList(generics.ListCreateAPIView):
serializer_class = CallSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrSuperuser,)
def pre_save(self, obj):
auth_user = self.request.user
obj.auth_user = auth_user
def get_queryset(self):
"""
This view should return a list of all the calls
for the currently authenticated user.
"""
auth = self.request.user
if isinstance(auth, AnonymousUser):
return []
elif auth.is_superuser:
return XCall.objects.all()
else:
return XCall.objects.filter(auth_user=auth)
In CallList's browseable API, I see the following in the POST form at the bottom:
My question is: why is there no default value set for send_on, and there is one for backup_calls? I assumed that the form would follow the XCall model specification and use datetime.now() for defaulting the former, and leave backup_calls blank (since it's nullable). How can I get the form to follow the model specifications?
You actually want to set an initial value, not a default value. See the docs. Your code should be:
from django.utils import timezone
class CallSerializer(serializers.HyperlinkedModelSerializer):
send_on = serializers.DateTimeField(initial=timezone.now())
...
A default value is the value provided to an attribute if no value is set for the field. The distinction between initial and default arguments mirrors the difference between Django's initial argument for form fields and Django's default argument for model fields.
There is a distinction between model defaults and initial values in forms. This is especially the case for default values which are actually functions because they are only called when the instance is saved. For example, which now do you want - this time at which the blank form is displayed, or the time at which the user presses "POST"? Django applies the default when saving the model if the field value is missing. To achieve what you want you need to manually set the default in the serialize field, for example:
class CallSerializer(serializers.HyperlinkedModelSerializer):
send_on = serializers.DateTimeField(default=datetime.now)
...

Django - What's a good way to handle manytomany with intermediary table in forms and views

Suppose I have the following models -
class Item(models.Model):
name = models.CharField(max_length=150)
value = models.DecimalField(max_digits=12,decimal_places=2)
class Organization(models.Model):
name = models.CharField(max_length=150)
items = models.ManyToManyField(Item, through='Customizable')
class Customizable(models.Model):
organization = models.ForeignKey(Organization)
item = models.ForeignKey (Item)
value = models.DecimalField(max_digits=12,decimal_places=2)
More often than not, when items are "assigned" to an organization, they will have the same value as originally recorded in the related Item object. But in certain cases, an item assigned to an organization may have an overridden value (hence the intermediary model). Since overriding the original value happens rarely (but it does happen) I want to allow the user to simply select desired items from a list of Item instances to assign them to an organization instance. The user will then have the option of overriding individual values later after bulk assignment is complete.
So I have the following simple ModelForm -
class AssignItemsForm(forms.ModelForm):
items = forms.ModelMultipleChoiceField(queryset=Item.objects.all(),required=False,widget=forms.CheckboxSelectMultiple)
class Meta:
model = Organization
exclude = ('name',)
Now since I have a through model, a simple form.save() won't work. I need to
(i) save Customizable instances corresponding to the items selected by the user and
(ii) make sure the persisted Customizable instances have the proper value taken from the corresponding value taken from the item instance related by foreignkey .
I am trying to handle it in a view (but my mind is blocked) -
def assign_items(request, oid):
organization = Organization.objects.get(id=oid)
if request.method == 'POST':
form = AssignItemsForm(data=request.POST, instance=organization)
if form.is_valid():
current_organization = form.save(commit=False)
#
#placeholder to save Customizable instances here
#
return HttpResponseRedirect(reverse('redirect-someplace-else'))
else:
form = AssignItemsForm(instance=organization,)
return render_to_response("assign_items.html", {"form": form,}, context_instance=RequestContext(request))
You would have to use save_m2m method:
def assign_items(request, oid):
organization = Organization.objects.get(id=oid)
if request.method == 'POST':
form = AssignItemsForm(data=request.POST, instance=organization)
if form.is_valid():
current_organization = form.save(commit=False)
current_organization.save()
form.save_m2m()
return HttpResponseRedirect(reverse('redirect-someplace-else'))
else:
form = AssignItemsForm(instance=organization,)
return render_to_response("assign_items.html", {"form": form,}, context_instance=RequestContext(request))
Look here for more info:
http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method
I'd approach this in a different way. You have an intermediary model for your m2m. Hence I'd argue that AssignItemsForm should be backed by this intermediary model. Therefore I'd change it as follows:
# forms.py
class AssignItemsForm(forms.ModelForm):
value = forms.DecimalField(max_digits=12, decimal_places=2, required = False)
class Meta:
model = Customizable
Next, the matter of allowing users to choose a different value. In order to do this I've made the value field of the model optional (required = False). I then check if the user has supplied an explicit value. If not I assume that the Item's default value is to be used. For this I am overriding the clean method of the form:
def clean(self):
super(AssignItemsForm, self).clean()
value, item = self.cleaned_data.get('value'), self.cleaned_data.get('item')
if not value:
value = item.value
self.cleaned_data['value'] = value
return self.cleaned_data
And finally I tested this in admin.
# admin.py
from app.forms import AssignItemsForm
class CAdmin(admin.ModelAdmin):
form = AssignItemsForm
admin.site.register(Item)
admin.site.register(Organization)
admin.site.register(Customizable, CAdmin)
This way you can continue to use form.save() thereby avoiding custom manipulation in the view. You'll have to change your view a bit to make sure that the organization is auto selected for assigning items.
# views.py
def assign_items(request, oid):
organization = Organization.objects.get(id=oid)
if request.method == 'POST':
form = AssignItemsForm(data=request.POST.copy())
form.save()
else:
form = AssignItemsForm(initial = {'organization': organization})
...
Override the save method of the ModelForm. This way you won't have to repeat yourself if you need to use the form in multiple places.
See this answer for more details:
https://stackoverflow.com/a/40822731/2863603