Child form instance in Django - django

As a beginner in a contact list project I could not pass the instance of the contact profiles to the phone list form. When I want to add several phone numbers to a person I need the person's instance to be loaded to the add-phone form. when I call the function 'addphone' the form opens a new phone for a list of contacts.
models.py
class contact_names(models.Model):
first_name=models.CharField(max_length=20,null=True,blank=True)
last_name=models.CharField(max_length=20)
area_l=(
('TWO','TWO'),
.
.
)
dep_l=(
.
.
('Other','Other'),
)
area=models.CharField(max_length=22,choices=area_l,null=True,blank=True)
DEP =models.CharField(max_length=22, blank=True, null=True, choices=dep_l)
Subdivision =models.CharField(max_length=20, blank=True, null=True)
created=models.DateTimeField(auto_now_add=True)
id=models.UUIDField(default=uuid.uuid4, unique=True,editable=False,primary_key=True)
def __str__(self) -> str:
return self.last_name
def __str__(self) -> str:
return self.first_name
class contact_phone(models.Model):
p_type=(
('Fixed Phone','Fixed Phone'),
('Cell Phone','Cell Phone'),
)
typep=models.CharField(max_length=15, choices=p_type)
person=models.ForeignKey(contact_names, on_delete=models.CASCADE)
phone=models.CharField(max_length=15)
def __str__(self) -> str:
return self.phone
views.py
def user(request):
form=newuser()
if request.method =='POST':
form=newuser(request.POST)
if form.is_valid():
form.save(commit=True)
return redirect('contacts')
return render(request,'apptwo/form_page.html',{'form':form})
def addphone(request,pk):
ps=contact_names.objects.get(id=pk)
form=newphone(instance=ps)
if request.method =='POST':
form=newphone(request.POST)
if form.is_valid():
form.save(commit=True)
return redirect('contacts')
return render(request,'apptwo/form_page2.html',{'form':form})
forms.py
class newuser(forms.ModelForm):
class Meta:
model= contact_names
fields='__all__'
class newphone(forms.ModelForm):
class Meta:
model=contact_phone
fields=['person','typep','phone']

It looks like you're trying to create a phone number form that is associated with a specific contact profile. In your addphone view, you are correctly fetching the contact profile from the database using the primary key (pk) passed in through the URL, but in the POST portion of the view, you are creating a new form without passing in the instance of the contact profile that you fetched earlier.
To fix this, you should update the POST portion of the addphone view to use the instance of the contact profile when creating the phone number form:
if request.method =='POST':
form=newphone(request.POST,instance=ps)
if form.is_valid():
form.save(commit=True)
return redirect('contacts')
Also, In your form, you should define the foreign key to person and it should be hidden in the form,
class newphone(forms.ModelForm):
person = forms.ModelChoiceField(queryset=contact_names.objects.all(), widget=forms.HiddenInput())
class Meta:
model=contact_phone
fields=['person','typep','phone']
This way when you save the form, the person foreign key will be automatically set to the instance of the contact you passed.

I have found a way but with minor issues.
Still when I want to add the first phone to the particular person, the person dropdown list is shown as '------', in form as default, but the instance is OK. And when I want to add a new second phone, the phone field in the form is not empty.
I'll be very glad if someone helps me. I appreciate the posted answer, it helped but didn't solve it completely.
views.py
def addphone(request,pk):
ps=contact_names.objects.filter(id=pk)
for s in ps:
pd=s.contact_phone_set.all()
if pd.first()== None:
form=newphone(instance=s)
form.fields['person'].queryset=ps
else:
form=newphone(instance=pd.last())
form.fields['phone'].queryset=None # <------This has no effect!
if request.method =='POST':
form=newphone(request.POST)
if form.is_valid():
form.save(commit=True)
return redirect('contacts')
return render(request,'apptwo/form_page2.html',{'form':form})enter code here
forms.py
class newphone(forms.ModelForm):
# person = forms.ModelChoiceField(queryset=contact_names.objects.all(), widget=forms.HiddenInput())# #THis didn't Help Since the field is required.
class Meta:
model=contact_phone
p_type=(
('Fixed Phone','Fixed Phone'),
('Cell Phone','Cell Phone'),)
fields=['person','typep','phone']
labels = {
'typep': '',
}
widgets = {
'typep': forms.RadioSelect(choices=p_type)
}

Related

Django | Don't show the foreign key valules in dropdown which are already assigned

I am working on a Hostel management system and new in Django field. I have a model Student and Bed . The models code is below:
class Bed(models.Model):
name = models.CharField("Bed No.",max_length=200)
room = models.ForeignKey(Room, on_delete=models.CASCADE, default='1')
class Meta:
verbose_name_plural = "Bed"
def __str__(self):
return self.name
class Student(models.Model):
name = models.CharField("name",max_length=200)
cell_no = models.CharField("cell No",max_length=200)
photo = models.ImageField(upload_to ='students_pics/')
emergency_cell_no = models.CharField("Emergency Cell No", max_length=200)
bed = models.ForeignKey(Bed, on_delete=models.CASCADE, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = "Student"
def __str__(self):
return self.name
I want that when I select a bed from Student Model dropdown, there should only display the beds which are not already assigned to some other students.
I have tried something like:
bed = models.ForeignKey(Bed, on_delete=models.CASCADE, null=True, blank=True).exclude(----)
but it does not work. I have searched around Please help.
you must filter this field from admin file.
solution 1 for filtering in admin)
for example, define a status field for the bed model with true value as default, after that override save method into Student model and with creating a student with a select bed, set status bed field to false
2 for filtering)
query on all students and check which beds don't assigned, then show into the admin
but for admin file you have to follow something like the below link
enter link description here.
or
link 2
or search django admin filtering key word
The answer above works as expected in the question. But when I update the Student Form, the form field of "bed" is not pre populated with the values. I have also used instance=student. My code for update view is below:
def updatestudent(request,id):
student = Student.objects.get(pk=id)
form = StudentForm(instance=student)
if request.method == 'POST':
#print('Printing Post:', request)
form = StudentForm(request.POST, instance=student)
if form.is_valid():
form.save()
messages.success(request, "Student updated successfully.")
return redirect('/student/view/'+id)
context= {'form':form}
return render(request, 'new-student.html', context)
Assuming you're using a form and do not want to do this in the admin, you can do the following. First, add a related_name= argument to your bed field on the Student model, like so (could be any string) and make migrations and migrate:
bed = models.ForeignKey(Bed, on_delete=models.CASCADE, null=True, blank=True, related_name='all_beds')
Then, using a model form with a custom __init__ method, you can populate your dropdown with only those beds that are not assigned.
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = ['name', 'cell_no', 'emergency_cell_no', 'bed', 'photo']
def __init__(self, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
self.fields['bed'].queryset = Bed.objects.filter(all_beds__bed__isnull=True)
Here's what's happening here. The init method is populating the choices on the bed field with whatever .queryset you pass to it.
In this case, you're using the related_name "all_beds" to refer back to the Student model. The all_beds__bed is looking at Student.bed, and all_beds__bed__isnull=True is asking the database to return all Bed instances that do not have some existing relation to Student.bed. In other words, if a Bed instance has some populated bed field on the Student model, do not add it to the queryset.
When you call this model form in your view, for example:
form = StudentForm()
...it will populate accordingly. If you need that form to be dynamically created with data only available in your view, say an ID number or user data, you can instantiate the modelform class in your view. It doesn't HAVE to be in a seperate forms.py file.
Using Dynamic Forms
Now let's suppose you want to have an update view that excludes all the assigned beds, BUT includes the bed that was assigned to the student being updated.
Here's an example of how that could work, using a Q Object query in a dynamically instantiated form class within the view. You can do this by writing and calling a function that creates the ModelForm. The Q Object query combines unassigned beds with the one bed that IS assigned to the student instance.
Note that the create_form function takes in the request object. This isn't necessary as written, but I'm showing it because you may have a case where your logged in user wants to see the bed they have selected, in which case you could use Q(all_beds__user=request.user), assuming you have added a user foreign key to Student your model. Of course another way of doing this is to query the student based on request.user.
from django.db.models import Q
def updatestudent(request,id):
student = Student.objects.get(pk=id)
form = StudentForm(instance=student)
def create_form(request, student):
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = ['name', 'cell_no', 'emergency_cell_no', 'bed', 'photo']
def __init__(self, *args, **kwargs):
super (StudentForm, self).__init__(*args, **kwargs)
self.fields['bed'].queryset = Bed.objects.filter(Q(all_beds__bed__isnull=True)|Q(all_beds__id=student.id))
return StudentForm
StudentForm = create_form(request, student)
if request.method == 'POST':
form = StudentForm(request.POST, instance=student)
if form.is_valid():
form.save()
messages.success(request, "Student updated successfully.")
return redirect('/student/view/'+id)
else:
form = StudentForm(instance=student)
context= {'form':form}
return render(request, 'new-student.html', context)
For more information about complex queries using Q objects, check out the documentation here.

Users to see only their objects in Django

So I have an application where users can create their own Companies. But what I want in the view is for them to see only their entries on the view. I have seen similar questions on this platform but they don't work as expected. Below is my code.
Models.Py
from django.contrib.auth.models import User
class Company (models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
company_name = models.CharField(max_length=100, null=True)
mailing_address = models.CharField(max_length=100, null=True)
physical_address = models.CharField(max_length=100, null=True)
class Meta:
verbose_name_plural = "Companies"
def __str__(self):
return self.company_name
views.py
#login_required(login_url='login')
def company (request):
all_companies = Company.objects.filter(user=request.user)
count= Company.objects.all().count()
context = {'all_companies': all_companies, 'count': count}
return render(request, 'company/company.html', context)
forms.py
class CompanyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CompanyForm, self).__init__(*args, **kwargs)
self.fields['company_name'].widget.attrs = {'class': 'input',}
self.fields['date_created'].widget.attrs = {'class': 'input',}
self.fields['mailing_address'].widget.attrs = {'class': 'input',}
self.fields['physical_address'].widget.attrs = {'class': 'input',}
class Meta:
model = Company
fields = ('company_name', 'date_created', 'mailing_address', 'physical_address',)
The so largely this works to ensure that every user only sees the company they have created. However, I can successfully create the companies from the admin side but a glaring issue appears. I have to manually select users from the form field = users in the admin form as shown in the picture below, to be able to create and save companies. It is the same behaviour on the front end with the form. This doesn't look right.
How can I ensure a company automatically points to the owner (user) who created it, without having to manually force the form to choose the user.
admin page
If you want the user to be added automatically in Django admin, you can override the save_model of its corresponding ModelAdmin:
https://docs.djangoproject.com/en/4.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_model
If you want it to be populated when users are creating companies using forms, you can set the user attribute like this:
# assuming request.user is available
company_form = form.save(commit=False)
company_form.user = request.user
company_form.save()
Since, user is the foreign key. You can take advantage of
'formfield_for_foreignkey' method in the ModelAdmin class.
This method gets executed for the foreign fields declared in the model. Here, we can check whether it has been executed for the user or not if yes, then we can prepopulate its value. You can customize the admin form by creating ModelAdmin class in admin.py for the Company model
#admin.register(Company)
class CompanyAdmin(admin.ModelAdmin):
form = CompanyForm
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'user':
kwargs['initial'] = request.user.id
return db_field.formfield(**kwargs)
return super(CompanyAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
In the ModelAdmin class you can specify the form class for further customizations.
Note, this will only prepopulate the value, the value of the user can be changed in the form. To avoid this, you can make user field uneditable and readonly field.
So I finally found the solution, at least for the user field in the Company form.
This gives a clear way of doing this: https://docs.djangoproject.com/en/4.0/topics/class-based-views/generic-editing/#models-and-request-user
In views.py: I added form.instance for the field user to ensure it picks the current user and feeds it in the form.
def company_form (request):
form = CompanyForm()
if request.method == 'POST':
# Request files helps upload other files such as images
form = CompanyForm(request.POST, request.FILES)
#This automatically inserts the user without exposing the form field
form.instance.user = request.user
if form.is_valid():
form.save()
return redirect('company')
Then I modified the model field for the user to ensure it is not editable.
models.py
class Company (models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, editable=False)
This ensured no one can edit the user including the admin. That ideally solves 90 percent of my issues.
Appreciate everyone's help on this.

Django form not updating existing record even when instance is passed

I have a form that is used to create and edit a model instance. But when editing the model instance, the form still tries to create a new record and fails because the unique together fields already exist. I am already passing the instance when initializing the form.
views.py
def organization_course_detail(request, org_id):
'''
Get all courses that are associated with an organization
'''
template_name = 'users/organization-course-list.html'
organization = models.Organization.objects.get(id=org_id)
if request.method == 'POST':
print organization.id
form = forms.CreateOrganizationForm(request.POST, instance=organization)
if form.is_valid():
print 'form is valid'
org = form.save(commit=False)
org.save()
return HttpResponseRedirect(
reverse('users:org-course-list',
kwargs={'org_id': org_id}))
else:
form = forms.CreateOrganizationForm(instance=organization)
forms.py
class CreateOrganizationForm(forms.ModelForm):
'''
A form used to create a new organization. At the same time,
we create a new course that is a clone of "Chalk Talk SAT"
and associate the course with the organization and any student
that signs up from that organization
'''
class Meta:
model = models.Organization
fields = ['name', 'country', 'acronym',]
models.py
class Organization(models.Model):
'''
This is a model where we will have every institution
(test prep centers, governments, schools) that we do workshops
at or create school accounts for
'''
name = models.CharField(max_length=2000)
country = CountryField(null=True, blank='(Select Country)')
date = models.DateTimeField(default=timezone.now)
acronym = models.CharField(max_length=7, help_text="(Up to 7 characters)")
expiration_date = models.DateTimeField(default=timezone.now)
def get_org_admins(self):
return self.admin_for_organizations.all()
class Meta:
unique_together = (
('name', 'country')
)
def __str__(self):
return self.name
Have you looked at the entries in your db table? (or can you post them?)
You might try using the django shell and try to execute your code by hand (query the db with an org_id and check the result. Edit a field and save it. Does that work?)
Also I think blank can only be True or False here.
country = CountryField(null=True, blank='(Select Country)')
https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.Field.blank

django logged in userprofile and relating additional form

I have question regarding how to attach additional form to logged in users in Django.
I want that additional form belongs to logged in user and the data I enter in the form should goes under logged in user table. I am new to Django and python please have patience I hope i can explain correctly what i want to do with this
Data I shall enter for this view shall go under logged in user only basically i want to attach this view to the logged in user only Error I am getting is
Exception Value:
registration_todos.user_id may not be NULL
#models
class userProfile(models.Model):
user = models.OneToOneField(User)
birth =models.DateField()
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
class todos(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=100)
created = models.DateField()
time = models.TimeField()
def __unicode__(self):
return unicode(self.user)
#forms additional form for todos
class formtodos(ModelForm):
title = forms.CharField(label=(u'Todo'))
created = forms.DateField(label=(u'Date'))
time = forms.TimeField(label=(u'Time'))
#user = forms.CharField(label=(u'username'))
class Meta:
model = todos
exclude=('user',)
#view
def modeltodo(request):
if request.user.is_authenticated():
todos.objects.filter(user=request.user)
if request.method == 'POST':
form =formtodos(request.POST)
if form.is_valid():# All validation rules pass
todoss = form.save(commit=False)
todoss.created_by = request.user
form.save()
return HttpResponseRedirect('/profile/')
else:
form = formtodos() # An unbound form
context = {'form':form}
return render_to_response('todo.html', context, context_instance=RequestContext(request))
you've specified exclude = ('user',) in your form. This means that when you try to save the form there is no user_id present which causes the error. You probably want to put this before the save() call: todoss.user = request.user

Saving the edited information in Django

I have created a form in my app where I can take details of a suer. Now i am able to edit the form but i am not able to save the data. I would like to save the changed data and move on.
It says: Adult with this User already exists.
My urls.py:
url(r'^home/editform/(?P<userpk>[^/]+)/$', 'lexuseditform', name='lexuseditform'),
url(r'^home/edited/(?P<userpk>[^/]+)/$', 'lexusedited', name='lexusedited')
My views.py:
#login_required
def lexuseditform(request,userpk):
if int(userpk) != request.user.pk:
return HttpResponseForbidden()
else:
adult = Adult(user=request.user)
if request.method == 'POST': # If the form has been submitted...
form = AdultForm(request.POST,instance=adult) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
form.save()
redirect_url = reverse('lexusedited')
return HttpResponseRedirect(redirect_url) # Redirect after POST
else:
form = AdultForm(instance=adult) # An unbound form
return render(request,'lexus/lexuseditform.html', {'form': form})
#login_required
def lexusedited(request,userpk):
return render(request,'lexus/lexusedited.html')
My forms.py:
from models import Adult
from django.forms import ModelForm
class AdultForm(ModelForm):
"""
Edit Profile Information
"""
class Meta:
model = Adult
fields = ('user', 'email','fullname')
My models.py:
from django.db import models
from django.contrib.auth.models import User
class Adult(models.Model):
"""
Adult Information
"""
user = models.OneToOneField(User)
fullname = models.CharField(max_length=100)
email = models.EmailField()
def __unicode__(self):
return self.user.username
Not sure where my error is. Need some guidance.. Thanks..
Although you haven't shown the Adult model structure, I bet it has something like
class Adult(models.Model):
user = models.ForeignKey(User, unique=True)
That's why you cannot save new Adult() with the same user(name). So you have to either change the models, or to load existing Adult for the specified user:
if Adult.objects.filter(user=request.user).exists():
adult = Adult.objects.get(user=request.user) # load existing Adult
else:
adult = Adult(user=request.user) # create new Adult
But I don't know how your form and models look like.
UPDATE:
or using:
adult, is_created = Adult.objects.get_or_create(user=request.user)
A bit of a guess since you didn't post the code for your form and model, but assuming the form is a regular model form, your problem very probably comes from your Adult model having a unique constraint on User (either a OneToOneField or a ForeignKey with unique=True). Since you create a new Adult instance for the form, it violates the unique constraint. Assuming (once again) this constraint is what you want, and your view is supposed to either create a related Adult instance for the user if it doesn't yet exist or edit the existing one, you need to first check if there's an Adult instance for the user:
#login_required
def lexuseditform(request):
try:
adult = Adult.objects.get(user=request.user)
except Adult.DoesNotExist:
adult = Adult(user=request.user)
#... your code here
Also note that I removed the userpk argument and test against request.user.pk which is useless if you think about it.
Correct Version(Refering to Tisho's Answer):
My views.py:
#login_required
def lexuseditform(request,userpk):
if Adult.objects.filter(user=request.user).exists():
adult = Adult.objects.get(user=request.user) # load existing Adult
else:
adult = Adult(user=request.user) # create new Adult
if request.method == 'POST': # If the form has been submitted...
form = AdultForm(request.POST,instance=adult) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
form.save()
redirect_url = reverse('lexusedited')
return HttpResponseRedirect(redirect_url) # Redirect after POST
else:
form = AdultForm(instance=adult) # An unbound form
return render(request,'lexus/lexuseditform.html', {'form': form})