I have standard django User model and I have an extended model Member. The Member model is connected to Subscription model.
class Type(models.Model):
limit = models.IntegerField()
name = models.CharField(max_length=50)
default = models.BooleanField()
def __str__(self):
return self.name
class Subscription(models.Model):
started = models.DateField()
type = models.OneToOneField(Type)
def __str__(self):
return self.type.name
class Member(models.Model):
user = models.ForeignKey(to=User)
number = models.CharField(max_length=10)
match_code = models.CharField(max_length=50)
parent = models.ForeignKey("self", null=True, blank=True)
name = models.CharField(max_length=100)
address = models.CharField(max_length=255)
postcode = models.CharField(max_length=10)
city = models.CharField(max_length=100)
country = models.ForeignKey(to=Country, null=True)
language = models.ForeignKey(to=Language)
telephone = models.CharField(max_length=50)
mobile = models.CharField(max_length=50)
main_email = models.EmailField(null=True, blank=True)
active = models.BooleanField()
subscription = models.ForeignKey(Subscription)
def __str__(self):
return self.name
When I create a new user, and when I want to save it to database I do it as follows :
language = Language.objects.get(key="EN")
country = Country.objects.get(key="BE")
# Add to user model
user = User()
user.username = request.POST['email']
user.first_name = request.POST['firstname']
user.last_name = request.POST['lastname']
user.email = request.POST['email']
user.set_password(request.POST['password'])
user.save()
# Add subscription
subscription = Subscription()
subscription.started = datetime.date.today()
subscription.type = Type.objects.filter(default=True)
last_subscription = Subscription.objects.all().order_by('-id')[0]
# Get the last field from user model
last_user = User.objects.all().order_by('-id')[0]
# Add to member model with the last user
member = Member()
member.number = request.POST['member_id']
member.address = request.POST['address']
member.postcode = request.POST['postcode']
member.city = request.POST['city']
member.country = country
member.telephone = request.POST['telephone']
member.mobile = request.POST['mobile']
member.user = last_user
member.language = language
member.active = False
member.subscription = last_subscription
member.save()
But I think that I hit the database to many times. Is there any better solution to do it? Can it be done maybe with one query?
Several things wrong here
subscription.type = Type.objects.filter(default=True)
You are asigning a whole queryset to subscription.type this code would produce an error here. Secondly don't use default as a model field name.
last_subscription = Subscription.objects.all().order_by('-id')[0]
Not quite sure what you are trying to do here. The efficient and error free approach would be to save the subscription object and use it's generated id for your next operation.
user.username = request.POST['email']
user.first_name = request.POST['firstname']
user.last_name = request.POST['lastname']
user.email = request.POST['email']
user.set_password(request.POST['password'])
It's not a good idea to directly pass in post data to models. You should use django forms to validate them first.
last_user = User.objects.all().order_by('-id')[0]
# Add to member model with the last user
This may or may not be the user that you just saved. It's very likely that another instance would have saved another user just after this thread did. It's safer to use user.id (as already mentioned regarding subscription)
Actually, you don't need to get last subscription or last user instance. You can(and should!) just use your subscription and user objects. Because they will have id after the save method will be called."Auto-incrementing primary keys"
Also, it seems that you've never saved the subscription instance.
Related
I have the following models/forms/view in which I have managed to submit to two different models as follows:
Models
class Account(models.Model):
username = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=150)
actflag = models.CharField(max_length=1, blank=True)
acttime = models.DateTimeField(blank=True, null=True)
comments = models.TextField(_('comments'), max_length=500, blank=True)
def __str__(self):
return self.name
class ISIN(models.Model):
code = models.CharField(max_length=12)
account_name = models.ForeignKey(Account, on_delete=models.SET_NULL, null=True)
actflag = models.CharField(max_length=1, blank=True)
acttime = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.code
Forms
from apps.portfolio.models import Account, ISIN
class PortfolioForm(forms.ModelForm):
class Meta:
model = Account
fields = ['name', 'comments']
class IdentifierForm(forms.ModelForm):
class Meta:
model = ISIN
fields = ['code']
View
def portfolios(request):
if request.user.is_authenticated:
if request.POST:
fm = PortfolioForm(request.POST)
fm2 = IdentifierForm(request.POST)
if fm.is_valid():
messages.success(request, 'Portfolio has been created.')
account = fm.save(commit=False)
account.username = request.user
account.acttime = timezone.now()
account.actflag = 'I'
account.save()
isin = fm2.save(commit=False)
#isin.account_name = account.name
isin.acttime = timezone.now()
isin.actflag = 'I'
isin.save()
return redirect('portfolios')
else:
fm = PortfolioForm()
fm2 = IdentifierForm()
context = {"name": request.user, "form": fm, "form2": fm2}
return render(request, 'portfolios.html', context)
else:
return redirect('login')
However, you will notice the commented line in my view: isin.account_name = account.name, when I uncomment this line and try to submit the forms again I get the following error: Cannot assign "'test'": "ISIN.account_name" must be a "Account" instance.
I believe it's to do with ForeignKey but still unsure how to store the newly created account name the user submitted within the isin model.
Help is much appreciated.
Although my answer solves the problem you originally had, there are a couple additional points that I wanted to make.
Improve naming and fix the original error
Your field is called account_name, and it implies that a string will be stored there. If it was actually a string, you would be able to do what you tried:
isin.account_name = account.name
In reality, you have a ForeignKey to the Account model, so you have to actually save a reference to the account object:
isin.account_name = account
It's a really good idea to have a foreign key instead of just a string because it avoids denormalization.
The problem here is the name of the field, account_name. If you later want to access the account name, you would have to write something like isis.account_name.name. Sounds wrong, doesn't it?
You could solve this by renaming your field like so:
class ISIN(models.Model):
code = models.CharField(max_length=12)
account = models.ForeignKey(Account, on_delete=models.SET_NULL, null=True)
actflag = models.CharField(max_length=1, blank=True)
acttime = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.code
Then, in your view, you would just isin.account = account, and later, if you wanted to access the name, you would use isin.account.name.
Another minor thing is that in some places an account is called Account and in other places it's Portfolio. This creates an illusion that they're unrelated entities and makes your code harder to read and maintain.
You probably should decide which one is the better term, and make it consistent everywhere.
Use builtin timestamp mechanism
Looks like you're using the acttime field to manually store creation time of accounts and ISINs.
You could use Django's auto_now_add property to do that automatically, like so:
class Account(models.Model):
acttime = models.DateTimeField(auto_now_add=True)
If you also wanted to store the last time an Account was updated, you could use auto_now (also renamed fields here for clarity):
class Account(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
And to stay DRY, you could make a mixin for that and use it in both Account and ISIN:
class TimeStampMixin(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Account(TimeStampMixin, models.Model):
username = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=150)
actflag = models.CharField(max_length=1, blank=True)
comments = models.TextField(_('comments'), max_length=500, blank=True)
def __str__(self):
return self.name
class ISIN(TimeStampMixin, models.Model):
code = models.CharField(max_length=12)
account = models.ForeignKey(Account, on_delete=models.SET_NULL, null=True)
actflag = models.CharField(max_length=1, blank=True)
def __str__(self):
return self.code
This way, the creation time and the latest update time are automatically stored in your models (the ones that inherit from TimeStampMixin).
Validate both forms
Looks like you're only checking one of the forms for validity, and not the other:
if fm.is_valid():
You should probably check both, in case ISIN.code is invalid:
if fm.is_valid() and fm2.is_valid():
What it means is that you have to make an instance of the account model by getting the name in order to save the form like so:
def portfolios(request):
if request.user.is_authenticated:
if request.POST:
fm = PortfolioForm(request.POST)
fm2 = IdentifierForm(request.POST)
if fm.is_valid():
messages.success(request, 'Portfolio has been created.')
account = fm.save(commit=False)
account.username = request.user
account.acttime = timezone.now()
account.actflag = 'I'
account.save()
# Here is where we get the instance of account
account = Account.objects.get(name=account.name)
isin = fm2.save(commit=False)
isin.account_name = account
isin.acttime = timezone.now()
isin.actflag = 'I'
isin.save()
return redirect('portfolios')
else:
fm = PortfolioForm()
fm2 = IdentifierForm()
context = {"name": request.user, "form": fm, "form2": fm2}
return render(request, 'portfolios.html', context)
else:
return redirect('login')
The field account_name is a ForeignKey to Account, but you are assigning an string. You should to assign an Account.
Change:
isin.account_name = account.name
To:
isin.account_name = account
So i'm working on job application portal.
the logic is as follows :
Applicant ---> Applies for ---> Job
Models are (Job, User, Application)
I used the User model from django and i extend it.
Now the dilemma is when i render the ApplicationForm, because i have to update the foreign key and i want it to be updated automatically.
Here is my code :
Models.py
class Job(models.Model):
owner = models.ForeignKey(User,related_name='job_owner',on_delete=models.CASCADE)
title = models.CharField(max_length=100)
#location
job_type = models.CharField(max_length=15,choices=JOB_TYPE)
description= models.TextField(max_length=1000)
published_at = models.DateTimeField(auto_now=True)
vacancy = models.IntegerField(default=1)
salary = models.IntegerField(default=0)
experience = models.IntegerField(default=1)
category = models.ForeignKey('Category',on_delete=models.CASCADE)
icon = models.ImageField(upload_to ='job_icons/',default='job_icons/job.png')
slug = models.SlugField(blank = True,null=True)
class Application(models.Model):
job = models.ForeignKey(Job, related_name="job_applied",on_delete=models.CASCADE)
applicant = models.ForeignKey(User,related_name='job_applicant',on_delete=models.CASCADE)
first_name= models.CharField(max_length=40)
last_name= models.CharField(max_length=40)
email = models.EmailField(max_length=60)
website = models.URLField()
cv = models.FileField(upload_to='application/')
coverletter = models.TextField(max_length=550)
application_date = models.DateTimeField(auto_now=True)
def __str__(self):
return self.last_name+"\t"+self.first_name
Forms.py
class JobApplication(ModelForm):
class Meta:
model = Application
fields = ['first_name', 'last_name','email', 'website','cv','coverletter']
vews.py
def job_detail(request,slug):
job_specific = Job.objects.get(slug=slug)
form = JobApplication(instance=request.user)
if request.method == 'POST':
form = JobApplication(request.POST,request.FILES)
if form.is_valid():
my_form = form.save(commit=False)
my_form.job = job_specific
Application.applicant.user = request.user
Application.job = job_specific
my_form.save()
context ={'job_specific':job_specific, 'form':form,}
return render(request,"job/job_details.html",context)
So once the user submit their application, i wanted to updated the fields that are "foreign key" without prompting the user.
I do not know how to arrange this in the views.py or if it's even possible this way?
thanks to everyone in advance
So i solved the problem, it was really simple solution:
my_form = form.save(commit=False)
my_form.job = job_specific
my_form.applicant = request.user
I have a Django models where I have this :
class Patient(models.Model):
FirstName = models.CharField(max_length=264)
LastName = models.CharField(max_length=264)
Address = models.TextField(blank=True)
Telephone_no = models.CharField(max_length=100,blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,related_name='patients')
class UploadedImages(models.Model):
patient = models.ForeignKey(Patient,on_delete=models.CASCADE,related_name='images')
original = models.ImageField(upload_to = user_directory_path, validators=[validate_file_extension],verbose_name = 'Image')
enhanced = models.ImageField(upload_to=analyses_directory_path, blank=True)
segmented = models.ImageField(upload_to=analyses_directory_path, blank=True)
class Processed(models.Model):
uploaded_image = models.ForeignKey(UploadedImages,on_delete=models.CASCADE,related_name='processed')
pre_analysed = models.ImageField(upload_to=analyses_directory_path, blank=True)
analysedimage = models.ImageField(upload_to=analyses_directory_path, blank=True)
so I want to make queries based on the current user which is user = request.user this is possible in the patient model case as I can make Patient.objects.filter(user=user)
but i can't make it the other 2 models
is there any idea how I can do this?
I didn't add the user FK as I thought I wouldn't need it but now I do?
do i need to add it ? can I make a query without adding the field ?
If you want to query across relationships, Django has explicit syntax for that. For example, to get all the UploadedImage objects for a specific user, use UploadedImage.objects.filter(patient__user=user).
Patient.objects.filter(user=user) returns a queryset, to get patient by user, assuming one Patient has only one user:
patient = Patient.objects.filter(user=user).first()
then you can do:
uploaded_images = patients.images.all()
for image in uploaded_images:
processed = image.processed.all()
I'm trying the get the records from the Student table, condition is that the student's primary key do not exist in the From table.(From is used as a relation) table.
Relation is "student from department"
Model:
class Student(models.Model):
name = models.CharField(max_length=20)
password = models.CharField(max_length=30)
phone_no = PhoneNumberField(null=False, blank=False, unique=True)
email = models.EmailField()
pic_location = models.FileField()
username = models.CharField(max_length=30)
class From(models.Model):
s = models.ForeignKey(Student, on_delete=models.PROTECT)
d = models.ForeignKey(Department,on_delete=models.PROTECT)
class Department(models.Model):
name = models.CharField(max_length=20)
password = models.CharField(max_length=30)
phone_no = PhoneNumberField(null=False, blank=False, unique=True)
email = models.EmailField()
I'm trying to get those records in the list view. And please review whether the way i'm retrieving session variable is good to go in such case??
class PendingStudent(ListView):
# Students pending for department approval
context_object_name = 'pending_students'
model = From
template_name = "admin_panel/department/student_detail.html"
def get_queryset(self):
department = self.request.session.get('username')
return From.objects.filter(~Q(d__name==department))
I've used session, to store what type of user is logged in (student/teacher/department).
It seems that you want to return a queryset which excludes certain values. For that I'd use .exclude() instead of filter since it's more explict.
You can check that here
def get_queryset(self):
department = self.request.session.get('username')
queryset = From.objects.exclude(d__name=department)
# In order to print the SQL query you can do this below
# print(queryset.query.__str__())
return queryset
However, if you want to return many students who are not in the From the table you could do something like:
def get_queryset(self):
return Student.objects.filter(from__d__isnull=True)
You can check that here
I want to assign the username field of Passengers model to the username of the current logged in user. I cant seem to do it. Below is the code.
Models.py
class UserProfileInfo(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
profile_pic = models.ImageField(upload_to='profile_pics',blank=True)
def __str__(self):
return self.user.username
class Passengers(models.Model):
username = models.CharField(max_length = 100)
passenger_firstname = models.CharField(max_length = 100)
passenger_lastname = models.CharField(max_length = 100)
passenger_age = models.CharField(max_length = 100)
passenger_gender = models.CharField(max_length=6, choices=GENDER_CHOICES, default='female')
froms.py
class PassengerForm(forms.ModelForm):
class Meta():
model = Passengers
fields = (
'passenger_firstname',
'passenger_lastname',
'passenger_age',
'passenger_gender')
views.py
def passenger_info(request):
if request.method == "POST":
passenger_details = PassengerForm(data=request.POST)
if passenger_details.is_valid():
passenger_details.username = request.user
passenger_details.save()
else:
passenger_details = PassengerForm()
return render(request,'passenger_info.html',{'passenger_details':passenger_details})
No error message is printed out but it leaves the username field blank like below:
django-admin model object
While the answer by #Navid2zp is correct, the other issue you're having is with this line:
passenger_details.username = request.user
passenger_details is a ModelForm, and after initialisation it doesn't have an attribute username. It has a field username that can be accessed as bound field (passenger_details['username']) if the form has data or as a form field (passenger_details.fields['username']).
By giving it the attribute username you're achieving nothing since that won't be associated in any way with the field username.
But the easiest way to assign additional parameters to the instance of the form before it is saved is the following:
profile = passenger_details.save(commit=False) # gives you the instance
profile.user = request.user # assuming you're using `ForeignKey` here, calling it `user` instead of `username` for clarity
profile.save()
First of all it doesn't make any sense to do it like this. You should create a ForeignKey to user model.
Also request.user will return the user object not the username so if you need the username you should call it: request.user.username
And to fix your problem:
class Passengers(models.Model):
username = models.ForeignKey(User, null=False, blank=False)
passenger_firstname = models.CharField(max_length = 100)
passenger_lastname = models.CharField(max_length = 100)
passenger_age = models.CharField(max_length = 100)
passenger_gender = models.CharField(max_length=6, choices=GENDER_CHOICES, default='female')
also you can get the first name and the last name from User model too. also gender and age should be in UserProfileInfo model which makes more sense and Passengers model should be about linking users to bus, airplane or etc not to hold data that are not going to change.