ModelForm with foreignkey field is not being saved - python

I'm trying to save a ModelForm with foreignkey field from another model. But somehow the data is not being saved.
The primary key of the foreignkey field in the Problem models is from Biodata models
index.html form is loaded with session['patient'] value that should be the value of the foreignkey in the Problem models upon creation of new item.
Upon submitting the form, it says the new item is successfully added, but at the backend, it wasn't saved at all.
Here's the code :
models.py
class Biodata(models.Model):
lastname = models.CharField(max_length=50)
firstname = models.CharField(max_length=50)
dob = models.DateField()
sex = models.CharField(max_length=10)
address = models.CharField(max_length=100,blank=True)
suburb = models.CharField(max_length=40,blank=True)
state = models.CharField(max_length=50,blank=True)
postcode = models.CharField(max_length=6,blank=True)
def __str__(self):
return self.firstname + " " + self.lastname
class Problems(models.Model):
biodata = models.ForeignKey(Biodata, on_delete = models.CASCADE, default=None)
problem = models.CharField(max_length=200)
notes = models.CharField(max_length=300)
status = models.CharField(max_length=30)
date = models.CharField(max_length=10)
def __str__(self):
return self.problem
views.py
def problem_add(request):
patient_id = request.session['patient']
form = ProblemForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.Biodata = Biodata.objects.get(id=patient_id)
instance.save
messages.success(request,('Problem added'))
return redirect('/crud/index/')
else :
messages.success(request,('There is error in the form'))
return redirect('/crud/index/')
forms.py
class ProblemForm(forms.ModelForm):
STATUS = [('S','Stable'),('U','Unstable'),('I','Inactive'),('O','Ongoing Dx.')]
problem = forms.CharField(label='',max_length=50, widget=forms.TextInput(attrs={'placeholder':'Problem'}))
notes = forms.CharField(label='', widget=forms.Textarea(attrs={'rows':4,'placeholder':'Notes'}))
status = forms.ChoiceField(label='', widget=forms.Select(attrs={'placeholder':'Status'}), choices=STATUS)
date = forms.DateField(label='', widget=forms.DateInput(format='%m/%d/%Y', attrs={'class':'datepicker','placeholder':'Date'}),
input_formats=('%m/%d/%Y', ))
class Meta :
model = Problems
fields = ('problem','notes','status','date')

In your views.py correct it
instance.biodata=Biodata.objects.get(id=patient_id)
You are trying
instance.Biodata = .......
Since Biodata field does not exist in Problems table

You have just written instance.save and this doesn't result in any error because it is valid to write it like that. The execution moves to new line and you get the success message.
Because save is a method on instance you should call it like instance.save().

Related

Interactive drop down list django with option to add new entry

I am relatively new to django. I have made a simple app which enable a user to record details regarding a run (Activity). The user enters data such as: distance, time, route name, date ran..etc using a django ModelForm.
I have a ModelForm which enables the logged on user to add an Activity to the model database.
How do I get the form to add a drop down list of all the routes which are already in the model database AND add a new one if the Person is entering data about a route they haven't ran before.?
class Person(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
fName = models.CharField(max_length =100)
lName = models.CharField(max_length =100)
weight = models.FloatField()
sex = models.CharField(max_length=10)
def __str__(self):
return self.lName
class Activity(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
length = models.FloatField()
runTime = models.DurationField()
route = models.CharField(max_length=100)
ave = models.DurationField()
dateRun = models.DateField()
marathon = models.DurationField()
def __str__(self):
return self.route ```
I would add model Route in models.py:
class Person(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
fName = models.CharField(max_length=100)
lName = models.CharField(max_length=100)
weight = models.FloatField()
sex = models.CharField(max_length=10)
def __str__(self):
return self.lName
class Route(model.model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
length = models.FloatField()
route = models.CharField(max_length=100)
class Activity(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
runTime = models.DurationField()
route = models.ForeignKey(Route, on_delete=models.CASCADE)
ave = models.DurationField()
dateRun = models.DateField()
marathon = models.DurationField()
def __str__(self):
return self.route
When person wants to add activity he will have all routes that he enters before. That solution require additional job in modelform, and need of another form to add new route. I think the ActivityForm should look like:
class ActivityForm(forms.ModelForm):
class Meta:
model = Activity
fields = '__all__'
widgets = {'person': forms.HiddenInput}
and init it in views.py:
form = ActivityForm(initial={'person': request.user.id})
form.field['route'].queryset = Route.objects.filter(person=request.user)

'NoneType' object has no attribute 'customer_id'

I have two models in an app called customer. The models are Customer and Account. The logic is one customer can have many accounts. So the auto generated default 'id' column in Customer is Foreign Key in Account. Account also has an auto generated default 'id' column. I have created a function for an account_id field in Account which will be generated on a simple algorithm. customer_id and id from Account will be concatenated as 'customer_id'_'id', (for eg. 1_1). The function will first check the current customer_id of the Account instance created and then check which is the last id for that customer_id. For newly added account, it will capture the id part from account_id (i.e. the part after underscore) and increment it by 1. Then concatenation happens for the same customer_id and id. Following is the code-:
models.py
from asyncio.windows_events import NULL
from django.db import models
from django.db.models.signals import pre_save
from django.contrib.auth.models import AbstractBaseUser
from .utils import set_account_id
class Customer(AbstractBaseUser):
name = models.CharField(max_length=50)
phone = models.BigIntegerField(null=True)
email = models.EmailField(max_length=100, null=True)
org_name = models.CharField(max_length=100, null = True)
org_logo = models.ImageField(upload_to='logos', blank=True)
subscription_start_date = models.DateField()
subscription_end_date = models.DateField()
password = models.CharField(max_length=50, blank=True, null=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'email']
def __str__(self):
return self.name
class Account(AbstractBaseUser):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
account_id = models.CharField(max_length=100, default=None, blank=True, null=True)
name = models.CharField(max_length=50)
phone = models.BigIntegerField(null=True)
email = models.EmailField(max_length=100, null=True)
password = models.CharField(max_length=50, blank=True, null=True)
account_type = models.CharField(max_length=50, choices=[
("survey", "SURVEY"),
("sme", "SME"),
("residents", "RESIDENTS")
])
account_subscription_date = models.DateField(null=True)
account_expiry_date = models.DateField(null=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'email']
def __str__(self):
return self.name
def pre_save_set_account_id(sender, instance, *args, **kwargs):
if not instance.account_id:
instance.account_id = set_account_id(instance) #Error here
print(instance.account_id)
pre_save.connect(pre_save_set_account_id, sender=Account)
utils.py
def set_account_id(instance):
Klass = instance.__class__
fk = 'customer_id'
obj = Klass.objects.first()
field_object = Klass._meta.get_field(fk)
fk_value = field_object.value_from_object(obj) #Error here
last_account = Klass.objects.filter(customer_id=fk_value).all().order_by('id').last()
accounts_pk = last_account.id
new_account_int = accounts_pk + 1
new_account_id = fk_value + '_' + new_account_int
return new_account_id
Now when I have entered one record in customer and trying to enter a record in account through the admin panel. When I am filling out the form for account in admin panel, I am leaving the account_id as blank. After clicking on Save, I am getting the following error-:
AttributeError at /admin/customer/account/add/
'NoneType' object has no attribute 'customer_id'
I have commented "Error here" on the specific lines at which the the error was pointed out by Django debugger. I am new to Django and have tried a lot but couldn't resolve the error. Please help.
Hey i don't know why you need this account_id as db field in first place? If you need it to perform filter, I suggest to use post_save signal like:
def post_save_set_account_id(sender, instance, *args, **kwargs):
if instance.account_id is None:
instance.account_id = f'{instance.customer.id}_{instance.id}'
instance.save()
But if you don't need to build sql query based on account_id field i suggest use #property in Account model:
#property
def account_id(self):
return f'{self.id}_{self.account.id}'
then you cen use it as model field for example:
account = Account.objects.get(pk=1)
account.account_id
prints for example:
1_1

How can I set foreign key from post request in Django

I have this models
class Driver(models.Model):
first_name = models.CharField(max_length=250)
last_name = models.CharField(max_length=250)
created_at = models.DateTimeField(default=NOW)
updated_at = models.DateTimeField(default=NOW)
def __str__(self):
return self.first_name
class Vehicle(models.Model):
driver_id = models.ForeignKey(Driver,on_delete=SET_NULL,unique=True,null=True, blank=True)
make = models.CharField(max_length=150)
model = models.CharField(max_length=150)
plate_number = models.CharField(max_length=10,validators = [validate_plate_numberLATIN,validate_plate_numberCYRYLLIC], unique=True)
created_at = models.DateTimeField(default=NOW)
updated_at = models.DateTimeField(default=NOW)
def __str__(self):
return self.make
I try to set foreign key in my post request into Vehicle model
#method_decorator(csrf_exempt, name='dispatch')
def post(self,request,*args, **kwargs):
body = json.loads(request.body.decode("utf-8"))
newCar = Vehicle.objects.create(driver_id=body['driver_id'],make=body['make'],model=body['model'],plate_number=body['plate_number'])
data = json.loads(serializers.serialize('json',[newCar]))
return JsonResponse({'success':data})
And get this error
ValueError: Cannot assign "1": "Vehicle.driver_id" must be a "Driver" instance.
How to get rid off this error? How I can create an instance of Driver and 'post' an id?
You can do it in 2 ways
If you need the driver instance in somewhere in the code you can use this
driver_instance = Driver.objects.get(pk=body['driver_id'])
Vehicle.objects.create(driver_id=driver_instance,..)
Vehicle.objects.create(driver_id_id=body['driver_id'], ...)
The raw value of a ForeignKey can be accessed by appending "_id" to the field name, this can also be used to create an instance using the raw value
Vehicle.objects.create(driver_id_id=body['driver_id'], ...)

Sorting (order_by) in list

I am building a Blog App and I am trying to sort or order_by in list which contains multiple queries.
models.py
class BlogPost(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=30)
date = models.DateTimeField(auto_now_add=True)
class Comments(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
blog_of = models.ForeignKey(BlogPost, on_delete=models.CASCADE)
body = models.CharField(max_length=30, default='')
date_added = models.DateTimeField(auto_now_add=True)
views.py
def mypage(request):
query_1 = list(BlogPost.objects.filter(user=request.user).order_by('-date'))
query_2 = list(Comment.objects.filter(user=request.user).order_by('date_added'))
results = sorted(chain(query_1, query_2),key=attrgetter('date') , reverse=True)
context = {'results':results}
return render(reques, 'mypage.html', context)
But is showing
'Comment' object has no attribute 'date'
And I think this is because date field name is different in both model and i am sorting with only one, But i have no idea how can I sort with different field name.
Any help would be much Appreciated. Thank You
Or just add it as a property:
class Comments(models.Model): # do NOT give a model a plural name!
# ....
#property
def date(self):
return self.date_added
# or if it is a datetimefield
# return self.date_added.date()
ALso you can just write a more customized sorting key (e.g. in case the involved models are from third-party apps).
def order(obj):
try:
return obj.date
except AttributeError:
return obj.date_added
# ...
results = sorted(chain(query_1, query_2), key=order, reverse=True)

getting value from Mysql and passing to Django

I connected my database to django. I want to enable user (teacher) insert the name of a student and get test results on certain subjects.
I run python3 manage.py inspectdb and inserted it into models.py
class Profilez(models.Model):
student = models.CharField(max_length=255)
schgroup = models.CharField(max_length=255)
class Meta:
managed = False
db_table = 'profilez'
class Schoolz(models.Model):
profilez_id = models.AutoField(primary_key=True)
lit = models.IntegerField(blank=True, null=True)
math = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'schoolz'
in forms.py i put:
class StudentForm(forms.ModelForm):
SUB = (
('lit', 'lit'),
('math', 'math')
)
student = forms.CharField(max_length=150, label='', widget=forms.TextInput)
class Meta:
model = Schoolz
fields = ('student',)
in views.py:
def home(request):
if request.method == "POST":
form = StudentForm(request.POST)
if form.is_valid():
form1 = form.save(commit=True)
name = form1.student
ab=schoolz.objects.all()
context={
'name':name,
}
return render(request, 'book/analysis.html', context)
else:
form = StudentForm()
return render(request, 'book/search.html', {'form': form})
Can you please help me to understand what i am doing wrong and how to get value for certain subject for exmaple math subject.
I would appreciate help and guidance to undertand and execute it. I am struggling a month.
Notes
Add a field in Profile that should be unique for each student. Currently I am assuming name and surname combination will be unique.
If you use ajax, you can get score without refresh. Current way I have used is not very good.
You don't have to write models if you already have in DB. You can remove your models. add already present models in models.py and makemigrations and migrate.
Add a ForiegnKey field in Class10
class Class10(models.Model):
profile_id = models.IntegerField()
math = models.IntegerField()
literature = models.IntegerField()
biology = models.IntegerField()
student = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='stud_name') # add this in model
class Meta:
managed = False # make this True otherwise makemigrations won't get the changes.
db_table = 'class_10'
class Profile(models.Model):
student_name = models.CharField(max_length=255)
student_surname = models.CharField(max_length=255)
class Meta:
managed = False
db_table = 'profile'
views.py
def home(request):
if request.method == "POST":
form = StudentlForm(request.POST)
if form.is_valid():
form_1 = form.save(commit=False)
name = form_1.student_name
surname = form_1.student_surname
subject = form_1.subject
fil = Q(student__student_name=name) & Q(student__student_surname=surname)
student_1 = StudentScore.objects.filter(fil).values()
score = student_1[0][subject] # answer
context={
'score':score
}
return render(request, 'school/analysis.html', context)
else:
form = StudentlForm()
return render(request, 'school/search.html', {'form': form})
forms.py
class StudentForm(forms.ModelForm):
SUB = (
('math', 'math'),
('literature', 'literature'),
('biology', 'biology')
)
student_name = forms.CharField(max_length=150, label='', widget=forms.TextInput)
student_surname = forms.CharField(max_length=150, label='', widget=forms.TextInput)
subject = forms.CharField(widget=forms.Select(choices=SUB))
class Meta:
model = Profile
fields = ('student_name', 'student_surname', 'subject')
#Nina,
Please look on the relationship> it's general idea for the Student & Gradesheet model
class Student(models.Model):
std_name = models.CharField(max_length=100)
def __str__(self):
return self.std_name
class Gradesheet(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE)
sub = models.CharField(max_length=50)
grade = models.CharField(max_length=50)
def __str__(self):
return self.student.std_name
So if you need to search for a student grade for particular subject:
std_info = Student.objects.get(std_name='Nina')
Then you will get a Student Class instance for Nina.
Now fetch the data by relationship:
std_grade = std_info.gradesheet_set.filter(sub='math')
You will get QuerySet. Then just :
std_grade[0].grade
You will get your student's grade for particular subject. Look its a model relationship. So you may use other filtering options also to get your desired result.
According to your given model:
Instead of the profile_id you should use the Profile object which will help you to take the control through django ORM.
class Profile(models.Model):
student_name = models.CharField(max_length=255)
student_surname = models.CharField(max_length=255)
class Meta:
managed = False
db_table = 'profile'
class Class10(models.Model):
#profile_id = models.IntegerField()
profile = models.OneToOneField(Profile, on_delete=models.CASCADE,related_name='profile')
math = models.IntegerField()
literature = models.IntegerField()
biology = models.IntegerField()
class Meta:
managed = False
db_table = 'class_10'
So your query can be build by:
std_profile = Profile.objects.get(student_name='SomeoneName')
Now turn it for get the grade. Result would be:
math_grade = std_profile.profile.math
biology_grade = std_profile.profile.biology
literature_grade = std_profile.profile.literature
average_grade = ((math_grade + biology_grade + literature_grade)/3)
Here, your model relationship:Profile to Class10 is OneToOne

Categories

Resources