django owner permission in detail view - django

Here is my code
class EnrollmentDetail(generic.DetailView):
model = Enrollment
template_name = 'enrollments/enrollment_detail.html'
context_object_name = 'enrollment
Simple djeneric django detail view. I want only the owner of this view to can access it. Every one else i don't want go get it. I want to restrict their access. I think to raise error in that case but don't know which one. It is frustrating, because it is not complicated problem but can't solve it. I checked many posts on that matter but none of them offer slick and clean solution ( decorators, supers(), querysets etc.)
Here is my model
class Enrollment(models.Model):
"""Defines Enrollment model"""
doctor_name = models.ForeignKey(DoctorProfile, on_delete=models.CASCADE)
patient_name = models.CharField(max_length=30)
symptoms = models.CharField(max_length=80)
diagnosis = models.CharField(max_length=30)
received_at = models.DateTimeField(auto_now=True)
room_number = models.PositiveIntegerField()
Thanks for help

maybe you can override the get_queryset in your Detailview,
def get_queryset(self):
return self.model.objects.filter(user=self.request.user)

Simple make a if statement and check if the logged-in user has superuser rights:
class EnrollmentDetail(generic.DetailView):
if request.user.is_superuser:
model = Enrollment
template_name = 'enrollments/enrollment_detail.html'
context_object_name = 'enrollment
else:
# redirect to page

Related

is it safe to compare owner_id to request.user.id for authentication in django?

in my app I have the model:
class Meal(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=500)
carbohydrates = models.FloatField()
protein = models.FloatField()
fat = models.FloatField()
fiber = models.FloatField()
owner = models.ForeignKey('auth.User', on_delete=models.CASCADE)
the following serializer:
class MealSerializer(serializers.ModelSerializer):
class Meta:
model = Meal
fields = "__all__"
and this viewset:
class MealViewSet(viewsets.ModelViewSet):
queryset = Meal.objects.all()
serializer_class = MealSerializer
def get_queryset(self):
return Meal.objects.filter(owner_id=self.request.user.id)
And now I have a question, is it safe to compare owner_id=self.request.user.id in get_queryset method for authentication?
or is it possible somehow to specify user.id in request e.g. using postman and pull all Meal objects?
for example:
Is that possible in postman or somewhere else?
I am a beginner in django and rarely used postman. Sorry if I wrote something wrong, English is not my native language.
I'm not sure it will work. In filter, you have to write owner=self.request.user (preferred and safe). Or, if you really want to bother with IDs: owner__id=self.request.user.id.
Diving into self.request.user fields is a bit dangerous, because in case some non-authenticated user will get there — your code will crash.
In short, in general it is safe to compare. This has been asked before: Django/Auth: Can request.user be exploited and point to other user?

how to allow users to rate a product only once,

a Reviews model linked to Users and Product, nothing special,
users can vote as much as they want which is working,
but i'm stuck restricting the rating to just once
the view so far:
class ReviewCreateView(CreateView):
model = Review
form_class = ReviewForm
template_name = "form.html"
success_url = reverse_lazy('product_list')
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
model
class Review(models.Model):
product = models.ForeignKey(Product, on_delete=models.PROTECT, null=True,related_name='reviews')
user = models.ForeignKey(User, on_delete=models.PROTECT, null=True,related_name='reviewers')
rating = models.IntegerField(null=True, blank=True, default=0)
comment = models.TextField(null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
the other models wouldn't help that muc ,Product just describe a product ,and default (regular) User model
the form is ModelForm based on the Review Model itself
any suggestion is welcome, even if i have to redo the model, thank you very much
set db constraint like so:
class Meta:
constraints = [
UniqueConstraint([fields=['user', 'product'], name='product_user_unique'),
]
the condition for your views would be like this:
def get(self, request, product):
if (Review.objects.filter(user=request.user)
.filter(product_id=product).exists():
# probably, you'd want to pass some
# conditional data to the template's context here
do_something()
That could be it. You restrict non-unique reviews by UI, but if it's somehow bypassed, user will get a server error. Of course, you could also apply validation in your CreateView (or analogous).
From architectural standpoint, you can always create a review_user table which will have the Product ID, and the ID of the users who voted on that product.. and upon second vote, you check if there's already a vote in the table.
Once you have this understanding, I am sure that you will find a way to incorporate the logic within your code.

what should I put in the views.py so that a specific user can add new complaints in a complaint management system

I have the model created for complaints. I tried a few methods to help a user create the complaint, but nothing works so I have given up and I just want to understand what to add in the views.py so that I can do that. The system basically has multiple users and I want the users to send in their complaints to the admin panel, so that they can be viewed, edited or deleted as per use on the dashboard. Right now my form does get created but it does not save the complaints in the admin panel.
models.py:
class Complaints(models.Model):
user = models.ForeignKey(User, on_delete= CASCADE, null = True, blank=True)
title = models.CharField(max_length=300)
description = models.TextField(null=True, blank= True)
highpriority = models.BooleanField(default=False)
document = models.FileField(upload_to='static/documents')
def __str__(self):
return self.title
what to add in the views.py? I've tried various things but I don't know what to do to make it work.
this if my views.py but I feel like the entire thing is wrong so I want an entirely new views:
class ComplaintCreate(CreateView):
model = Complaints
form = ComplaintForm
fields = '__all__'
success_url = reverse_lazy('New')
template_name = 'new.html'
I want the final page to look like this:
there is not form attribute in CreateView change form to form_class
class ComplaintCreate(CreateView):
model = Complaints
form_class = ComplaintForm
fields = '__all__'
success_url = reverse_lazy('New')
template_name = 'new.html'

How to filter a Django model based on requirement from another model?

I'd like to retrieve all my contacts from my Contact model excluding those listed on my DoNotContact model. Is the following the most efficient way to do that: contacts = Contact.objects.filter(dont_contact=False) Wondering if this is going to take long to process, is there a more efficient way?
class Contact(models.Model):
email = models.CharField(max_length=12)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
audience = models.ForeignKey(Audience, on_delete=models.CASCADE)
def dont_contact(self):
try:
get_object_or_404(DoNotContact, email=self.email)
return True
except:
return False
def __str__(self):
return self.email
class DoNotContact(models.Model):
email = models.CharField(max_length=12)
#views.py
def send_email(request):
if request.method == "POST":
contacts = Contact.objects.filter(dont_contact=False)
Kwargs used model queryset filter methods are resolved into database columns. dont_contact here is a method and doesn't exist as a column in Contact model so calling Contact.objects.filter(dont_contact=False) will raise a FieldError.
For current implementation of your models you can do following
dont_contacts = DoNotContact.objects.values('email')
contacts = Contact.objects.exclude(email__in=dont_contacts)
A better solution with higher performance is to remove DoNotContact and add a BooleanField to Contact which handles your requirement.

How do you get the correct model instance using the PK without using the URL

I am trying to pass the PK of a blog post but I am having difficulty with it. If possible I would like to pass the PK without putting it in the URL.
I am trying to let my user edit their posts which are displayed in a list like below:
I need to make sure that when the user presses edit under a post, it allows them to edit that specific post. I believe where I am going wrong is in the get_object function when I try and get the PK. Any help with this would be much appreciated!
View
class EditPost(UpdateView):
model = ProjectPost
form_class = ProjectPostForm
template_name = 'howdidu/edit_post.html'
def get_object(self):
return ProjectPost.objects.get(pk=self.request.Get.get('pk'))
def get_success_url(self):
project_username = self.request.user.username
project_slug = self.object.project.slug
return reverse('user_project', kwargs={'username':project_username, 'slug': project_slug})
model
class ProjectPost(models.Model):
project = models.ForeignKey(UserProject)
title = models.CharField(max_length=100)
post_overview = models.CharField(max_length=1000)
date_created = models.DateTimeField(auto_now_add=True)
post_views = models.IntegerField(default=0)
post_likes = models.IntegerField(default=0)
url
url(r'^edit_post/$', login_required(views.EditPost.as_view()), name='edit_post'),
link used for edit post template
Edit post