Autofill my author field with foreign key - django

I am trying to autofill my user foreign key in my note project with authentication in django. I tried, but it's not working and asking that owner is required field. Please, help! Thanks in an advance.
views.py
#login_required(login_url='login')
def index(request):
tasks = Task.objects.filter(owner=request.user)
form = TaskForm()
if request.method=='POST':
form = TaskForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.owner = request.user
instance.save()
context = {
'tasks':tasks,
'form':form,
}
return render(request, 'list.html',context)
models.py
class Task(models.Model):
title = models.CharField(max_length=200)
completed = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
def __str__(self):
return self.title

Since you fill in the owner yourself, it makes no sense to specify the owner as a form field. You thus should exclude it, and let this be handled by the view. The form thus looks like:
class TaskForm(forms.ModelForm):
class Meta:
model = Task
exclude = ['owner']
If no ModelForm will need to specify the owner, you can mark the field as non-editable:
class Task(models.Model):
# …
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
editable=False
)
# …

Related

AttributeError: 'NoneType' object has no attribute 'aptTime' in Django

I have an appointment model for patient to book appointment with a doctor, i used a custom user model for both doctor and patients, using is_staff and is_patient to differentiate them...
class Appointment(models.Model):
doctor = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='staff',
on_delete=models.SET_NULL, null=True)
patient = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+', on_delete=models.SET_NULL, null=True)
aptTime = models.DateTimeField(blank=True, null=True)
pending_approval = models.BooleanField(default=True, null=True)
approved = models.BooleanField(default=False, null=True)
in my views.py i am trying to set the patient to request.user and doctor to the context i passed through the url, so i have the form_valid method to create appointment like this...
class AppointmentCreateView(CreateView):
model = Appointment
fields = ['aptTime']
template_name = 'patients/Appointment-Createview.html'
def form_valid(self, form, *args, **kwargs):
form.instance.doctor = self.kwargs['pk'] #TODO change to doctor instance
form.instance.aptTime = self.object.aptTime
form.instance.patient = self.request.user
form.save()
i got an error saying ValueError: Cannot assign "1": "Appointment.doctor" must be a "User" instance. Internal Server Error: /patient/Appointment-Create/1/
After searching around SO i was able to change my code to...
...Some code...
def form_valid(self, form, *args, **kwargs):
User = get_user_model()
doc = User.objects.get(id=self.kwargs['pk'])
form.instance.doctor = doc #TODO change to doctor instance
form.instance.aptTime = self.object.aptTime
form.instance.patient = self.request.user
form.save()
now i'm getting a new error saying
form.instance.aptTime = self.object.aptTime
AttributeError: 'NoneType' object has no attribute 'aptTime'
I am using a model form, i don't understand why i'm getting this error.
Someone please help me out.
A CreateView has no self.object, this is always assigned None. You do not have to access this anyway, since this is already assigned to the object wrapped in the form, so:
from django.contrib.auth.mixins import LoginRequiredMixin
class AppointmentCreateView(LoginRequiredMixin, CreateView):
model = Appointment
fields = ['aptTime']
template_name = 'patients/Appointment-Createview.html'
def form_valid(self, form):
form.instance.doctor_id = self.kwargs['pk']
form.instance.patient = self.request.user
return super().form_valid(form)
Your Appointment model however seems a bit odd. For example why not work with a nullable field approved that is NULL/None if it is not yet approved, True if the doctor approved, and False if the doctor rejected, this is probably better since otherwise the pending_approval can be False whereas approved can still be None resulting in a state that at least seems illogical:
class Appointment(models.Model):
doctor = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name='appointments',
on_delete=models.SET_NULL,
null=True,
)
patient = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name='+',
on_delete=models.SET_NULL,
null=True,
)
apt_time = models.DateTimeField()
approved = models.BooleanField(default=None, null=True)
Note: You can limit views to a class-based view to authenticated users with the
LoginRequiredMixin mixin [Django-doc].
Note: normally the name of the fields in a Django model are written in snake_case, not PascalCase, so it should be: apt_time instead of aptTime.

How to predefine value inside model/form in Django?

I'm creating simple app which allows users to create group.
When user create group it has following fields:
name
desc
inviteKey - i would this field to be hidden and generate 10 characters code and then send it.
My models:
class Group(models.Model):
groupName = models.CharField(max_length=100)
description = models.CharField(max_length=255)
inviteKey = models.CharField(max_length=255)
class Members(models.Model):
userId = models.ForeignKey(User, on_delete=models.CASCADE)
groupId = models.ForeignKey(Group, on_delete=models.CASCADE)
isAdmin = models.BooleanField(default=False)
Form:
class GroupForm(forms.ModelForm):
groupName = forms.CharField(label='Nazwa grupy', max_length=100)
description = forms.CharField(label='Opis', max_length=255)
inviteKey: forms.CharField(label='Kod wstępu')
class Meta:
model = Group
fields = ['groupName', 'description', 'inviteKey' ]
View:
def createGroup(request):
if request.method == "POST":
form = GroupForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, f'Group created')
return redirect('/')
else:
inviteKey = generateInviteKey()
form = GroupForm(initial={'inviteKey': inviteKey})
return render(request, 'group/createGroup.html',{'form': form})
Now i have form and inviteKey is visible and editable. I want this key to be visible but not editable.
The best way to do that my opinion is to set the default value for your invitation key in your model, that way, token is created "in the background" with a unique key, but we can go further.
For example :
import uuid
token = models.UUIDField(
default=uuid.uuid4,
unique=True,
editable=False,
)
This way you are sure that the token is unique (UUID is unique by design, but still) you cannot edit it so no wrong token can occur and last of all each object will get a unique token with no work on your side.
I am using UUID because it is recommended by Django as per the Documentation for token and unique identifier.
Note : If you set the UUID with a default value, you cannot get it before the object is created, depending on your use you might want to set it in the form (see answer below).
You can make the field disabled, so:
class GroupForm(forms.ModelForm):
groupName = forms.CharField(label='Nazwa grupy', max_length=100)
description = forms.CharField(label='Opis', max_length=255)
inviteKey = forms.CharField(label='Kod wstępu', disabled=True)
class Meta:
model = Group
fields = ['groupName', 'description', 'inviteKey' ]
This will also prevent a user from fabricating a POST request that contains a different invite key.
A problem that one now has to solve however is that we do not want to generate a different inviteKey when the user submits the form. This can be handled with session data, although it is not a very elegant solution. In that case we thus change the view to:
def createGroup(request):
if request.method == 'POST' and 'inviteKey' in request.session:
inviteKey = request.session['inviteKey']
form = GroupForm(request.POST, initial={'inviteKey': inviteKey})
if form.is_valid():
form.save()
messages.success(request, f'Group created')
return redirect('/')
else:
request.session['inviteKey'] = inviteKey = generateInviteKey()
form = GroupForm(initial={'inviteKey': inviteKey})
return render(request, 'group/createGroup.html',{'form': form})
You probably alo might want to make your inviteKey field unique, to prevent creating multiple groups with the same inviteKey:
class Group(models.Model):
groupName = models.CharField(max_length=100)
description = models.CharField(max_length=255)
inviteKey = models.CharField(max_length=255, unique=True)

How to filter data dynamically by supply values from form using django

I want to filter Blog Post objects or records based on the Post Category and a User that uploaded the Post record, it gives me an error when I try to do filter, this is the error.
ValueError at /dashboard/filter-post/
The QuerySet value for an exact lookup must be limited to one result using slicing.
Here is my models.py
class Category(models.Model):
cat_name = models.CharField(max_length=100, verbose_name='Category Name')
cat_desc = models.TextField(blank=True, null=True)
def __str__(self):
return self.cat_name
class Meta():
verbose_name_plural='Category'
class Post(models.Model):
pst_title = models.CharField(max_length=150)
pst_image = models.ImageField(blank=True, null=True, upload_to='uploads/')
user = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ManyToManyField(Category)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.pst_title
#property
def img_url(self):
if self.pst_image:
return self.pst_image.url
on forms.py
class FilterForm(forms.ModelForm):
user = forms.ModelChoiceField(
queryset=User.objects.all(),
widget=forms.Select(attrs={'class': 'form-control'}))
category = forms.ModelMultipleChoiceField(
queryset=Category.objects.all(),
widget=forms.SelectMultiple(attrs={'class': 'form-control js-example-disabled-results'}))
catch_bot = forms.CharField(required=False,
widget=forms.HiddenInput, validators=[validators.MaxLengthValidator(0)])
class Meta():
fields = ['user', 'category' ]
model = Post
on views.py
def filter_post(request):
post = FilterForm(request.GET)
queryset = Post.objects.all()
if post.is_valid():
user=post.cleaned_data.get('user')
category=post.cleaned_data.get('category')
if user and category:
queryset = queryset.filter(user__username=user, category__cat_name=category)
return render(request, 'backend/filter-post.html', {'query':queryset, 'post':post})
I am having challenges properly filtering this in my views any help?
Try this:
instead of this:
queryset = queryset.filter(user__username=user, category__cat_name=category)
use this:
queryset = queryset.filter(user=user, category=category)
Also don't name your model fields after the model name, just use name instead of pst_name or cat_name, you will see that when you will try access these values there will be no confusion.
UPDATE
Ok, maybe try to rewrite your view like this:
def filter_post(request):
posts = Post.objects.all()
form = FilterForm(request.GET) # its best practice to call your form instance `form` in the view so that the next line has better readability
if form.is_valid():
user=post.cleaned_data['user']
category=post.cleaned_data['category']
if user:
posts = posts.filter(user=user)
if category:
posts = posts.filter(category=category)
return render(request, 'backend/filter-post.html', {'posts':posts})

how to update a extended Django User model?

I have created the user authentication system which includes both the default User model and an extended User model. They are as below:
from django.db import models
from django.urls import reverse
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
Photo = models.ImageField(upload_to='documents/%Y/%m/%d/', null=True)
uploaded_at = models.DateTimeField(auto_now_add=True, null=True)
dob = models.DateField(max_length=20, null=True)
country = models.CharField(max_length=100, null=True)
State = models.CharField(max_length=100, null=True)
District = models.CharField(max_length=100, null=True)
phone = models.CharField(max_length=10, null=True)
def get_absolute_url(self):
return reverse('profile', kwargs={'id': self.id})
forms.py
class UserProfileForm(forms.ModelForm):
Photo = forms.ImageField( max_length=100)
dob = forms.DateField(widget=forms.TextInput(attrs={'type': 'date'}))
country = forms.CharField(max_length=100)
State = forms.CharField(max_length=100)
District = forms.CharField(max_length=100)
phone = forms.CharField(max_length=10)
class Meta:
model = UserProfile
fields = ('Photo', 'dob', 'country', 'State', 'District', 'phone')
With the help of the above model and form, I am able to create user, and enter values for those custom model fields and see the user profile. So far so good.
However, I am facing issues while I update those custom fields. I have used the Django's in-built modules to update the default User fields(email). But I am not able to find a way to update those custom fields('dob', 'country', 'State', 'District', 'phone'). Below is the method from views.
views.py
#login_required(login_url="/login/")
def editUserProfile(request):
if request.method == "POST":
form = UserProfileUpdateForm(request.POST, instance=request.user) # default User profile update
obj = UserProfile.objects.get(id=request.user.id)
form1 = UserProfileForm(request.POST or None, instance=obj) # custom fields update.
if form.is_valid() and form1.is_valid():
obj.Photo = form1.cleaned_data['Photo']
obj.dob = form1.cleaned_data['dob']
obj.country = form1.cleaned_data['country']
obj.State = form1.cleaned_data['State']
obj.District = form1.cleaned_data['District']
obj.phone = form1.cleaned_data['phone']
form.save()
form1.save()
messages.success(request, f'updated successfully')
return redirect('/profile1')
else:
messages.error(request, f'Please correct the error below.')
else:
form = UserProfileUpdateForm(instance=request.user)
form1 = UserProfileUpdateForm(instance=request.user)
return render(request, "authenticate\\editProfilePage.html", {'form': form, 'form1': form1})
I have an update button on my profile page, on clicking I could only see the "email" field with pre-populated data to update(I can update this default field successfully).
I have seen other stackoverflow posts, but they are not helping.
I am not able to figure out the mistakes.
Please help
Thank you,
I think the problem is in this line
obj = UserProfile.objects.get(id=request.user.id)
here left id is id from UserProfile model. so it will be something like this
obj = UserProfile.objects.get(user__id=request.user.id)

How to use a submit a one-to-many form

I have a list of employees who work at a site. Each site is owned by a User (using Django's standard user model).
I want to create a form that adds an employee and automatically links them to a site dependent on who the authenticated user is:
models.py:
class Employee(models.Model):
site = models.ForeignKey(Site, null=True)
employee_name = models.CharField(default='name', max_length=128, blank=False, null=False)
class Site(models.Model):
user = models.ForeignKey(User)
site_name = models.CharField(max_length=128, blank=False, null=False)
views.py:
site_profile = Site.objects.get(user=request.user)
if request.method == "POST":
form = EmployeeAddForm( request.POST )
if form.is_valid():
obj = form.save(commit=False)
obj.site = site_profile
obj.save()
return redirect('dashboard_home')
form = EmployeeAddForm()
return render(request, "dashboard/employees.html", {'form': form })
forms.py:
class EmployeeAddForm(forms.ModelForm):
class Meta:
model = Employee
fields = ( 'employee_name')
This code will add the employee to the database, but in django admin, list_display = 'site' results in Site object not the actual site name. It does not appear that the employee is linked to the site.
If I use obj.site = site_profile.id (adding .id), I get the error Cannot assign "1": "Employee.site" must be a "Site" instance.
Found the error: the above code is correct, I simply had a tab ordering error in my Site modeL
class Site(models.Model):
...
def __str__(self):
return self.site_name
def should have been inserted 1 tab inwards.