Django queryset filtering not working - python

I want the admin users to see only the model instances they created. I followed these instructions Filter django admin by logged in user
class FilterUserAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
if getattr(obj, 'user', None) is None: #Assign user only the first time, superusers can edit without changing user
obj.user = request.user
obj.save()
def queryset(self, request):
qs = super(FilterUserAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(user=request.user)
def has_change_permission(self, request, obj=None):
if not obj:
# the changelist itself
print('query change')
return True # So they can see the change list page
return obj.user == request.user or request.user.is_superuser
class CampaignAdmin(FilterUserAdmin):
...
This is how my code looks like.
Saving is fine.
However, other users are seeing the model campaign in their campaign list,
though they were not able to edit it. When a user, who is not the owner clicks the campaign to edit, 403 Forbidden page is seen.
I don't want the model instance to be shown in the other users' campaign list.

You should override get_queryset, not queryset. The method was renamed from queryset to get_queryset in Django 1.6.
def get_queryset(self, request):
qs = super(FilterUserAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(user=request.user)
Note that you do not need to use getattr when checking if getattr(obj, 'user', None) is None:, you can simplify it to if obj.user is None.

Related

Django - issue with Not Null constraint

Hi in my program I keep receiving the above exception and am unsure why. The issue happens when my requestLessons_view method tries to save the form.
Views.py
def requestLessons_view(request):
if request.method == 'POST':
form = RequestLessonsForm(request.POST)
if form.is_valid() & request.user.is_authenticated:
user = request.user
form.save(user)
return redirect('login')
else:
form = RequestLessonsForm()
return render(request, 'RequestLessonsPage.html', {'form': form})
forms.py
class RequestLessonsForm(forms.ModelForm):
class Meta:
model = Request
fields = ['availability', 'num_of_lessons', 'interval_between_lessons', 'duration_of_lesson','further_information']
widgets = {'further_information' : forms.Textarea()}
def save(self, user):
super().save(commit=False)
request = Request.objects.create(
student = user,
availability=self.cleaned_data.get('availability'),
num_of_lessons=self.cleaned_data.get('num_of_lessons'),
interval_between_lessons=self.cleaned_data.get('interval_between_lessons'),
duration_of_lesson=self.cleaned_data.get('duration_of_lesson'),
further_information=self.cleaned_data.get('further_information'),
)
return request
The error I receive is:
IntegrityError at /request_lessons/
NOT NULL constraint failed: lessons_request.student_id
Your .save() method is defined on the Meta class, not the form, hence the error. I would advise to let the model form handle the logic: a ModelForm can be used both to create and update the items, so by doing the save logic yourself, you basically make the form less effective. You can rewrite this to:
class RequestLessonsForm(forms.ModelForm):
class Meta:
model = Request
fields = [
'availability',
'num_of_lessons',
'interval_between_lessons',
'duration_of_lesson',
'further_information',
]
widgets = {'further_information': forms.Textarea}
def save(self, user, *args, **kwargs):
self.instance.student = user
return super().save(*args, **kwargs)
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].

How to redirect to previous page in custom DeleteView?

I have the following custom DeleteView:
class CarDeleteView(LoginRequiredMixin, DeleteView):
model = Car
context_object_name = 'car'
template_name = 'cars/car_confirm_delete.html'
success_message = "%(name)s is pending removal"
def get_success_url(self):
return reverse('car-list')
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
name = self.object.name
owner = self.object.owner
if owner != self.request.user:
messages.error(request, f'You don\'t have permissions to remove {name}')
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
# DO Stuff
return redirect(reverse('car-list'))
I'm trying to redirect to previous entry point when user isn't the owner (so he can't delete). But HttpResponseRedirect(request.META.get('HTTP_REFERER')) gives me the URL of the current page (car_confirm_delete.html) and not the previous one. How can I make it go to previous one? Maybe that's because Django does POST?
You need to import HttpResponseRedirect
from django.http import HttpResponseRedirect
and then
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))

I am getting ValueError at /car/offer/4/ Cannot assign "4": "CarRent.car" must be a "Car" instance. when trying to save Car instance to form

I am working on a car rental website for uber drivers in django, from the detailView I need drivers to be able to choose the duration of their rental, and other information will be auto filled to the form from my views.py, i was able to get the driver through request.user, i also need the PK of the car to be rented. searching through here i’ve tried various suggestions by people here, but i keep getting one error after another…
using
self.kwargs['pk'] results in ValueError at /car/offer/4/ Cannot assign "4": "CarRent.car" must be a "Car" instance.
then i tried using
form.car = Car.objects.get(pk= self.kwargs.get('pk')) which results in a AttributeError at /car/offer/4/ 'CarRent' object has no attribute 'is_valid'
can someone please tell me how to get the car instance saved in the CarRent model? any help will be greatly appreciated. Thanks
below is my code (reduced to the relevant bit)
models.py
class Car(models.Model):
car_owner = models.ForeignKey(User, related_name='car_owner', on_delete=models.CASCADE)
class CarRent(models.Model):
car = models.ForeignKey(Car, related_name='rented_car', on_delete=models.CASCADE)
driver = models.ForeignKey(User, related_name='driver_renting', on_delete=models.CASCADE)
rented_weeks = models.BigIntegerField(default=1, choices=WEEK_CHOICES)
forms.py
class RentForm(forms.ModelForm):
class Meta:
model = CarRent
fields = ['rented_weeks']
i’m only displaying the rented weeks as that’s the only information i need from the user.
views.py
class CarView(FormMixin, DetailView):
model = Car
form_class = RentForm
def get_success_url(self):
return reverse('car-details', kwargs={'pk': self.object.pk})
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
form = self.get_form()
form = form.save(commit=False)
form.car = self.kwargs['pk']
form.driver = request.user
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.save()
return super().form_valid(form)
form.car expects a Car object, not a string with the primary key, but you can simply use:
from django.contrib.auth.mixins import LoginRequiredMixin
class CarView(LoginRequiredMixin, FormMixin, DetailView):
# …
def post(self, request, *args, **kwargs):
form = self.get_form()
self.object = self.get_object()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.instance.car_id = self.kwargs['pk']
form.instance.driver = self.request.user
form.save()
return super().form_valid(form)
Note: You can limit views to a class-based view to authenticated users with the
LoginRequiredMixin mixin [Django-doc].
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

How to override Django Admin

I have two models Restaurant and Details. The superuser assigns each restaurant a user.When that user logs into admin i want only those Details associated with that user's Restaurant to be shown,and he should be able to edit them as well.
I tried to override admin's queryset function but to no success.Any help would be appreciated. This is what i did so far
I am just a beginner in Django.
class RestaurantAdmin(admin.ModelAdmin):
model = Details
def save_model(self, request, obj, form, change):
obj.user = request.user
super(RestaurantAdmin, self).save_model(request, obj, form, change)
def queryset(self, request):
print(request.user)
qs = super(ResaturantAdmin, self).queryset(request)
# If super-user, show all comments
if request.user.is_superuser:
return qs
return qs.filter(owner=request.user)
admin.site.register(Restaurant)
admin.site.register(Details,RestaurantAdmin)
The method you need to override is called get_queryset, not queryset.

Filter django admin by logged in user (Foreign Key showing up)

I want the admin users to see only the model instances they created. I followed these instructions Filter django admin by logged in user
class FilterUserAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
if getattr(obj, 'user', None) is None: #Assign user only the first time, superusers can edit without changing user
obj.user = request.user
obj.save()
def queryset(self, request):
qs = super(FilterUserAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(user=request.user)
def has_change_permission(self, request, obj=None):
if not obj:
# the changelist itself
print('query change')
return True # So they can see the change list page
return obj.user == request.user or request.user.is_superuser
class CampaignAdmin(FilterUserAdmin):
...
This is how my code looks like. Everything works fine. I need one more model with campaign as foreign key.
class ScreenAdmin(FilterUserAdmin):
...
admin.site.register(Campaign,CampaignAdmin)
admin.site.register(Screen,ScreenAdmin)
now when I go to screens, and I see campaigns created by other users to select from
I don't want campaigns from other users to be able to be selected
Update: This is my current get_form
class ScreenAdmin(FilterUserAdmin):
list_display = ('screen_name', 'id','screen_URL')
def get_form(self, request, obj=None, **kwargs):
self.exclude = ["beacon"]
if not request.user.is_superuser:
self.exclude.append('user') #here!
form = super(ScreenAdmin, self).get_form(request, obj, **kwargs)
#print(vars(form))
form.base_fields['campaign'].queryset = Campaign.objects.filter(user=request.user)
return form
def changelist_view(self, request, extra_context=None):
if request.user.is_superuser:
self.list_display = ('screen_name','user', 'id','screen_URL')
#print('Change List######')
return super(ScreenAdmin, self).changelist_view(request, extra_context)
this solution is perfectly sane and working
def queryset(self, request):
qs = super(FilterUserAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(user=request.user)
unless you are marked as superuser, an this is probably what you do.
to have access to the admin interface, you must check user as "staff" (is_staff)
if you check "superuser" you will see all of the data, so create other user (for the tests) add him proper rights, but do not mark mim as superuser, only is_staff and test it.

Categories

Resources