Django - ModelForm display the item but insert the foreignkey - django

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.

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.

Filter M2M in template?

In my model, I have the following M2M field
class FamilyMember(AbstractUser):
...
email_list = models.ManyToManyField('EmailList', verbose_name="Email Lists", blank=True, null=True)
...
The EmailList table looks like this:
class EmailList(models.Model):
name = models.CharField(max_length=50, default='My List')
description = models.TextField(blank=True)
is_active = models.BooleanField(verbose_name="Active")
is_managed_by_user = models.BooleanField(verbose_name="User Managed")
In the app, the user should only see records that is_active=True and is_managed_by_user=True.
In the Admin side, the admin should be able to add a user to any/all of these groups, regardless of the is_active and is_managed_by_user flag.
What happens is that the Admin assigns a user to all of the email list records. Then, the user logs in and can only see a subset of the list (is_active=True and is_managed_by_user=True). This is expected behavior. However, what comes next is not.
The user deselects an email list item and then saves the record. Since M2M_Save first clears all of the m2m records before it calls save() I lose all of the records that the Admin assigned to this user.
How can I keep those? I've tried creating multiple lists and then merging them before the save, I've tried passing the entire list to the template and then hiding the ones where is_managed_by_user=False, and I just can't get anything to work.
What makes this even more tricky for me is that this is all wrapped up in a formset.
How would you go about coding this? What is the right way to do it? Do I filter out the records that the user shouldn't see in my view? If so, how do I merge those missing records before I save any changes that the user makes?
You might want to try setting up a model manager in your models.py to take care of the filtering. You can then call the filter in your views.py like so:
models.py:
class EmailListQuerySet(models.query.QuerySet):
def active(self):
return self.filter(is_active=True)
def managed_by_user(self):
return self.filter(is_managed_by_user=True)
class EmailListManager(models.Manager):
def get_queryset(self):
return EmailListQuerySet(self.model, using=self._db)
def get_active(self):
return self.get_queryset().active()
def get_all(self):
return self.get_queryset().active().managed_by_user()
class EmailList(models.Model):
name = models.CharField(max_length=50, default='My List')
description = models.TextField(blank=True)
is_active = models.BooleanField(verbose_name="Active")
is_managed_by_user = models.BooleanField(verbose_name="User Managed")
objects = EmailListManager()
views.py:
def view(request):
email = EmailList.objects.get_all()
return render(request, 'template.html', {'email': email})
Obviously there is outstanding data incorporated in my example, and you are more than welcome to change the variables/filters according to your needs. However, I hope the above can give you an idea of the possibilities you can try.
In your views you could do email = EmailList.objects.all().is_active().is_managed_by_user(), but the loading time will be longer if you have a lot of objects in your database. The model manager is preferred to save memory. Additionally, it is not reliant on what the user does, so both the admin and user interface have to talk to the model directly (keeping them in sync).
Note: The example above is typed directly into this answer and has not been validated in a text editor. I apologize if there are some syntax or typo errors.

Django, best practice for associating model with author and how to do it

I'm making a django cms of sorts, and have got to the stage where I'm linking my model to the user id who created it. I only want to do this once, on subsequent updates the author field shouldn't change.
My model is blog. I presume I have to override the save() method, but I'm not sure what is best practice, or where I should be overiding the save method, ie in admin.py, in models.py or even maybe in forms.py?
from common.models import myUser #inherits from AbstractUser
class Blog:
title = models.CharField(max_length="100", null=True, blank=True)
author = models.ForeignKey(myUser, editable=False)
last_edit_by = models.ForeignKey(myUser, editable=False)
def save():
if self.author is None:
self.author = #how to get user id?
self.last_edit_by = ##how to get user id?
super(Features, self).save(*args, **kwargs)
else:
self.last_edit_by = #how to get user id?
Thanks in advance,
I think the best place to associate a user with some model data is in the views.
In your specific example, you can use a model form for blog.
class BlogForm(forms.ModelForm):
class Meta:
fields = ['title', 'last_edited_by']
Do not include author in the form if you do not want to do any validation based on value of author. In case you have to do any validation based on author you will have the include it the form fields and also add the author id value in the form post data.
Now in your view you can use this BlogForm for creating a blog instance with:
....
form = BlogForm(data)
if form.is_valid():
blog = form.save(commit=False)
blog.author = request.user
blog.save()
....
Now you have a blog with author.
Even if you really want to override the save method you can pass the author to the save method of Blog and associate it in the save method.
If you choose to include author in the form fields, to add the value of author in the post data you will have to make copy of the post data and then insert the value of author in the copied post data.

default value for ChoiceField in modelform

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.

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