How can I populate a model with non-form data? - python

I have Student, Class and StudentClass models. I would like a student to be able to join a new class by inputting the class_code into a form. To join a class the user's student_id and the class_code is saved to the StudentClass model. The student_id isn't a form field so should be obtained by querying the Student model with the logged-in user's username and returning the student_id.
models.py:
class StudentClass(models.Model):
class Meta:
unique_together = (('class_code', 'student_id'),)
class_code = models.ForeignKey(Class, on_delete=models.CASCADE)
student_id = models.ForeignKey(Student, on_delete=models.CASCADE)
forms.py:
class JoinClassForm(forms.ModelForm):
class Meta:
model = StudentClass
fields = ['class_code']
exclude = ('student_id',)
views.py
#login_required
def join_class(request):
if request.method == "POST":
joinclass_form = JoinClassForm(request.POST or None)
if joinclass_form.is_valid():
formclass_code = joinclass_form.data.get('class_code')
if Class.objects.filter(class_code=formclass_code).exists():
joinclass_form.save(commit=False)
joinclass_form.student_id =
Student.objects.filter(username=request.user.username).values_list('student_id', flat=True)
joinclass_form.save()
return redirect('assignments')
else:
messages.success(request, ("This class doesn't exist!"))
else:
joinclass_form = JoinClassForm
return render(request, 'join_class.html', {'joinclass_form': joinclass_form})
I've tried using a hidden_field for student_id, excluding student_id from the form fields and saving as commit=False, and I've gotten the same error: IntegrityError Not NULL constraint failed. Is there an error in my code that I have missed or am I using the wrong method?
Thanks in advance
Edit: Forgive me but copy-paste went kinda awry on the last set of code and I have no idea how to fix it

you dont need to put .username at request.user. You can just say request.user

Thanks everyone for your help but I managed to fix it now
In case anyone needs this in the future, I realised that I made 2 mistakes:
I wasn't saving my form correctly in my views.py, I was trying to save the value student_id to the form rather than an object.
https://docs.djangoproject.com/en/4.1/topics/forms/modelforms/#the-save-method
I named my foreign key in my StudentClass table as student_id which meant that Django was renaming it as student_id_id. To fix this, I just removed _id from student_id in the StudentClass table.

Related

How can I utilise the built in first and last name features of the Django User Model?

So I know that Django has a built in system with the User, and it contains things like Username, e-mail, password, and first and last name. I want to know how I can utilise this in my site.
So I have a first_name and last_name field in the models.py file, and they are CharFields. I want to know how to connect them to the already existing UserForm that comes with Django.
I have tried a few things already, such as doing this with the models.py file.
class UserProfileInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.OneToOneField(User, on_delete=models.CASCADE)
Here is some code for the form.py file.
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta():
model = User
fields = ('username','email','password','first_name')
As you can see from the form, I added the first_name attribute, and in the models.py file, I have the first_name connected with the forms.py one. I am now getting this error.
HINT: Add or change a related_name argument to the definition for 'UserProfileInfo.user' or 'UserProfileInfo.first_name'.
So I added a related name field to the model, as shown here
first_name = models.OneToOneField(User, on_delete=models.CASCADE,related_name='first_name')
But, wouldn't you know it, I got yet another error:
ValueError: Cannot assign "''": "User.first_name" must be a "UserProfileInfo" instance.
I don't really know what is going on here.
So I expected to get no errors, just like the user field. Instead I got this error. Any help would be greatly appreciated :)
Your code is setting a OneToOne to the User model for both the user and first_name fields. So this means the related field will be User.id for both.
To specify that you want UserProfileInfo.first_name to map to User.first_name you will need to set to_field=first_name
class UserProfileInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.OneToOneField(User, on_delete=models.CASCADE, to_field='first_name', related_name='user_profile_first_name')
However, this will still cause an issue as you can only set a OneToOne relationship to a unique field. As User.first_name is not unique, you cannot set a OneToOne relationship to it.
If both the OneToOne relationships are referencing the same object, one of the relationships is redundant as you can access the fields on that object through the other relationship. It would be better to have a method on UserProfileInfo that gets the first name through the user field.
class UserProfileInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def get_first_name(self):
return user.first_name
You will also need to set signals to create/update UserProfileInfo when User is created/updated. Have a look at this article.
There are also other ways to extend the User model in Django. Have a look at this article or this answer for suggestions.
But if you're not adding any extra fields to the User model, it may be better just to use it directly.

Django ModelForm ManyToManyField initial value

I'm using Django 1.11.2 to develop a website. I use ModelForms to edit my model instances on my website. Every field of the form gets the fitting value of the instance I want to edit via 'initial' in my view. It works fine for all fields except ManyToManyFields.
The relevant code looks like this:
models.py:
class model1(models.Model):
name = models.CharField(max_length=45, blank=False, null=False)
class model2(models.Model):
name = models.CharField(max_length=45, blank=False, null=False)
relation = models.ManyToManyField(model1)
the ModelForm in forms.py:
class model2_form(forms.ModelForm):
class Meta:
model = model2
fields = '__all__'
and the view I use to edit model2 intances:
def model2_edit(request, objectid):
link = 'Model2'
model2_inst = model2.objects.get(id=objectid)
form = model2_form(initial={'name': model2_inst.name,
'relation': ???})
if request.method == 'POST':
f = model2_form(request.POST, instance=model2_inst)
if f.is_valid():
f.save()
return HttpResponseRedirect('/model2')
return render(request, "edit_db.html",
{"form": form, "link":link})
Everytime I edit an instance of model2 via the ModelForm, the 'relations' of the instance that already exist aren't preselected ('initial' isn't working). If I save the form like this without selecting the relations again, they get deleted and that instance of model2 has no relations anymore.
At the place of the '???' in my code I tried many ways to get those relations already selected in the form, but I couldn't find a working way.
I hope I managed to describe my problem, thanks in advance for any help or ideas.
form = model2_form(initial={'name': model2_inst.name,
'relation': [i.id for i in model2_inst.relation.all()]})
You should provide the instance for GET and POST requests. This way, you do not need to provide initial data - Django will get the values from the instance automatically.
model2_inst = model2.objects.get(id=objectid)
form = model2_form(instance=model2_inst)

Django M2M with addititional data with through

My problem is as follows. I am saving data for patients from a form on a webpage. The form is generated from model definitions in models.py. The information that I save is name, surname amongst others. I have a field for diagnosis which is selected using a multichoiceField and I save it using manytomany.
When the data is saved, a separate table is created for the diagnosis assigned to each patient as expected. The table contains a diagnosis and the ID of the patient it applies to. Each diagnosis is saved as a separate record.
In addition to selecting the diagnosis, I also save the date that the diagnosis is made. You will see what I mean in the models.py and form.py code below.
I would like to have the date for which the diagnosis was made also saved in the table but I can't figure out how to do this. I have tried following the docs: https://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany as well as some other posts on SO, but cannot figure out how to do it. I can't figure out how the views, forms and models need to be set up in order to achieve. Is it possible to do this and if so how? I have tried using an intermediate model with manytomany and 'through', but I do not understand it. Any help with this would be greatly appreciated.
Below is a simplified version of my code:
models.py:
class diagnosisChoices(models.Model): #This represents the list in the drop down menu for the different diagnosis.
diagnosis = models.CharField(max_length = 50)
def __str__(self):
return self.diagnosis
class PatientData(models.Model):
Name = models.CharField(max_length=100)
Surname = models.CharField(max_length=100)
dateOfBirth = models.DateField(default = datetime.datetime.now())
diagnosis = models.ManyToManyField(
'diagnosisChoices',
#on_delete=models.CASCADE,
)
views.py:
def patientDataView(request):
uId = request.user.id
if request.method == "POST":
form = PatientDataForm(request.POST)
if form.is_valid():
model_instance = form.save(commit=False)
model_instance.timestamp = timezone.now()
model_instance.save()
#model_instance.add(uId)
form.save_m2m()
return HttpResponseRedirect('/dataBase')
else:
form = PatientDataForm()
return render(request, "dataBaseTest.html", {'form': form})
date_of_diagnosis = models.DateField(default=datetime.datetime.now())
forms.py
from django.forms import ModelForm
from django import forms
from .models import PatientData
from .models import diagnosisChoices #This is the list of diagnosis in the dropdown
from django.forms import extras
import datetime
from functools import partial
class PatientDataForm(ModelForm):
class Meta:
now = datetime.datetime.now()
thisYear = now.year
DateInput = partial(forms.DateInput, {'class': 'datepicker'})
widgets = {
}
model = PatientData
fields = ['Name',
'Surname',
'dateOfBirth',
'diagnosis',
'date_of_diagnosis',
]
Thanks,
Thomas
The main thing that you are not getting is on the models.py, so I will focus on it.
You need three tables to do what you have described: diagnosisData, PatientData and a 'membership' table which I call diagnosisPatient. Then you build your model like this:
class diagnosisChoices(models.Model):
diagnosis = models.CharField(max_length = 50)
class PatientData(models.Model):
Name = models.CharField(max_length=100)
Surname = models.CharField(max_length=100)
dateOfBirth = models.DateField(default = datetime.datetime.now())
diagnosis = models.ManyToManyField('diagnosisChoices',through='diagnosisPatient')
class diagnosisPatient(models.Model):
patient = models.ForeignKey('PatientData')
diagnosis = models.ForeignKey('diagnosisChoices')
dateOfDiagnosis = models.DateField()
Once you have your model built this way, you should save your PatientData and your diagnosisChoices instances as usual. FOr the many to many relation, you should save it manualy on the diagnosisPatient table using the apropriate foreign keys and date. You can query the many to many relation from the PatientData model as usual with objects.all() function.
The thing here to keep in mind is that ManyToMany relations in django are always creating a new membership table for you behind the scenes. So when you do not need to insert extra information on the relationship the diagnosisPatient table is just made of two foreign keys, and it is hidden. The through argument on this relationship is just bringing this table to light and giving you control back to put whatever new relationship you like.

Issue with ModelForm and related objects

I have come to an impasse when using a ModelForm.
I'm extending the User model that comes with Django, and I'm also using a ModelForm so the user can edit it.
Following the same example in the documentation, I would have this code.
models.py
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
# In this case, department is optional, so I have set 'blank' and 'null' to True.
department = models.CharField(max_length=100, blank=True, null=True)
forms.py
class DepartmentForm(ModelForm):
class Meta:
model = Employee
fields = ['department',]
The problem comes at the view. I found that I need to pass an instance of the model to the form so the save() function works without having to customize it, but of course, user.employee has not been created yet, therefore it throws an error.
views.py
def DepartmentView(request):
# Here is the issue.
department = request.user.employee
if request.method == 'POST':
# I need to pass the instance here.
form = DepartmentForm(request.POST, instance=department)
if form.is_valid():
form.save()
else:
# And also here so it autocompletes the form.
form = DepartmentForm(instance=department)
return render(request, 'employee.html', {'form': form})
It works if I manually add a value to user.employee.department through the shell and then reload the page, otherwise the error is as follow.
RelatedObjectDoesNotExist at [something]
User has no employee.
Or something like that... I'm sorry, I didn't try the code above so the error could be a little different, but the concept is exactly the same.
I'm also sorry if this has been asked before. I did a Google search and couldn't find an answer to this issue.
You could use get_or_create to fetch the employee from the db, or create it if it doesn't exist.
department, created = Employee.objects.get_or_create(user=request_or_user, department='')
if request.method == 'POST':
form = DepartmentForm(request.POST, instance=department)
...
Another option is to use a signal, so that the related model is created when the user is created. Then you can assume that the employee already exists, and you can use request.user.employee instead of get_or_create.

Correct Model structure for django app?

I have a blog app that consists of 3 models: department, author, post
I am having trouble structuring the models correctly and creating the corresponding forms
models.py
from django.db import models
class Department(models.Model):
name=models.CharField(max_length=20)
posts = models.ForeignKey('Post')
authors = models.ManyToManyField('Author')
def __unicode__(self):
return self.name
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
post = models.ForeignKey('Post')
def __unicode__(self):
return self.last_name
class Post(models.Model):
title=models.CharField(max_length=20)
post = models.TextField()
creation_date = models.DateField(auto_now_add=True)
def __unicode__(self):
return self.title
The idea is that a department can have many posts, but each post belongs to only one department. A department can also be made up of multiple authors and authors can be in multiple departments. Where I'm really having trouble is with the forms.
The relevant urls.py looks like this:
url(r'^(?P<department_id>\d+)/posts/$', views.posts, name='posts'),
url(r'^(?P<department_id>\d+)/add_post/$', views.add_post, name="add_post"),
So I can pull in all the posts by department. The goal of the form is for the department id to be recognized and added automatically to the post.
def add_post(request, department_id):
department = Department.objects.get(pk=department_id)
if request.method == 'POST':
new_post_form = PostForm(data=request.POST)
if new_post_form.is_valid():
new_post = new_post_form.save(commit=False)
new_post.department = department
new_post.save()
return redirect('posts', department_id=department_id)
Now I realize that the Post model does not have a department attribute, which is the error that I get, but I'm guessing that there's a way to make this happen, I just don't know what it is.
Thanks as always for your help. Please let me know if anything is unclear.
The fact that the Post model does not have a department attribute should have given you the clue that your structure is wrong: it clearly needs one. The issue is that you have your ForeignKey the wrong way round: a FK is a one-to-many relationship, and lives on the "many" side, in your case Post, pointing to the "one", ie the Department.
Then your view code will work exactly as it is, and you can retrieve all posts for a department with my_department.post_set.all().

Categories

Resources