Django. DetailView simple validation. Check weather user has a subscription - python

When user requests object, when user wants to enter detail view's page, I want to check weather user have subscription and redirect him. But I don't know how to request user and validate him in DetailView. This is what at least I could did.
class PropertyDetailView(LoginRequiredMixin, DetailView):
login_url = '/login/'
model = Property
template_name = 'project/property/property_detail.html'
def post(self, *args, **kwargs):
if self.request.user.sale_tariff is None:
return redirect('/')
Are there any ways how to validate DetailView?

Overriding the get request something like this
def get(self, *args, **kwargs):
if self.request.user.sale_tariff is None:
return redirect('/')
else:
return render(template_name)

Related

How can I filter a queryset inside a django form?

I'm trying to add chat functionality to my app and right now I can add users to existing chats but what I need to do is filter users that are not already in the chat in the form.
I'm using django form with passed arguments to filter my users but I'm not getting any results.
My Chat model has a m2m field to user called 'users' and my User has a m2m field called 'friends' with itself.
forms.py
class AddUserToChatForm(forms.ModelForm):
class Meta:
model = Chat
fields = ('users', )
def __init__(self, chat, friends, request, *args, **kwargs):
self.request = request
self.chat = chat
self.friends = friends
super(AddUserToChatForm, self).__init__(*args, **kwargs)
self.fields['users'] = forms.ModelMultipleChoiceField(queryset=self.request.user.friends.exclude(user__in=chat.users.all()),
widget=forms.CheckboxSelectMultiple
(attrs={'class': 'add-people-to-chat-form'}),
label='Friends:')
views.py
def add_users_to_chat(request, pk):
chat = Chat.objects.get(pk=pk)
friends = request.user.friends.all()
if request.method == 'POST':
form = AddUserToChatForm(chat, friends, request, request.POST)
if form.is_valid():
users_to_add = form.cleaned_data['users']
chat.users.add(*users_to_add)
chat.save()
return redirect('messages')
else:
form = AddUserToChatForm(chat, friends, request, instance=None)
return render(request, 'add_users_to_chat.html', {'form': form, 'chat': chat, 'friends': friends})
EDIT
Looks like my exclude statement has no effect because when I switch it to filter the set returns all of the user friends.
UPDATE
I was able to retreive the required users using difference method like this:
in view:
chat = Chat.objects.get(pk=pk)
friends = request.user.friends.all()
chat_users = chat.users.all()
queryset = friends.difference(chat_users)
and pass it to the form
def __init__(self, queryset, request, *args, **kwargs):
self.request = request
self.queryset = queryset
super(AddUserToChatForm, self).__init__(*args, **kwargs)
self.fields['users'] = forms.ModelMultipleChoiceField(queryset=self.queryset,
widget=forms.CheckboxSelectMultiple
(attrs={'class': 'add-people-to-chat-form'}),
label='Friends:')
getting users with difference method but now when I try to submit the form I get this error: Calling QuerySet.filter() after difference() is not supported.
Debugger quits execution on this line in view:
users_to_add = form.cleaned_data['users']
As the fields are automatically generated by the ModelForm, it is best to define only the a queryset in init:
def __init__(self, chat, friends, request, *args, **kwargs):
self.request = request
self.chat = chat
self.friends = friends
super(AddUserToChatForm, self).__init__(*args, **kwargs)
self.fields['users'].queryset = self.request.user.friends.exclude(user__in=chat.users.all())
I was able to achieve the wanted result by querying using exclude after all:
in view:
queryset = friends.exclude(id__in=chat_users)
and now everything works as intended

Get current logged in user in Django forms

I need to get the current logged in user in Django form. I need it to get the domain from his email and accordingly fetch the list of other users with similar domain. Here is my code so far:
forms.py
class AuthUserCheckbox(forms.Form):
choice = forms.MultipleChoiceField(choices=[], widget=forms.CheckboxSelectMultiple, required=True)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
self.user_email = self.user.email.split('#')[1]
super(AuthUserCheckbox, self).__init__(*args, **kwargs)
self.fields['choice'] = forms.MultipleChoiceField(choices=[(i.id, i.email)
for i in User.objects.all()
if not i.is_active and
self.user_email in i.email
])
views.py
#login_required
def auth_users(request):
form = auth_users(data=request.POST, user=request.user)
return render(request, 'todoapp/auth_users.html', context={'form': AuthUserCheckbox()})
You need to pass the request's user into your form constructor and then place that form in context. Your view needs to look something like:
#login_required
def auth_users(request):
form = AuthUserCheckbox(request.POST, user=request.user)
return render(request, 'todoapp/auth_users.html', context={'form': form})
Of course, the above is an incomplete view, because you shouldn't just grab POST like that, but that is not the question.

Does Django's get_queryset() in admin prevent malicious object saving?

I am developing a multi-tenant app in Django. In the Django admin, some querysets are filtered based on the user, using get_queryset().
Up till now, when a user updated an object from the Django change form, I would validate the data by creating a ModelAdmin form using a factory function to capture the HttpRequest object, then ensure that the Guest object's user was the current user:
EXAMPLE
models.py
class Guest(models.Model):
guest_name = models.CharField(max_length=64)
user = models.ForeignKey(User, on_delete=models.CASCADE)
admin.py
#admin.register(Guest)
class GuestAdmin(admin.ModelAdmin):
def get_queryset(self, request)
qs = super().get_queryset(request)
return qs.filter(user=request.user)
def get_form(self, request, obj=None, **kwargs):
self.form = _guest_admin_form_factory(request)
return super().get_form(request, obj, **kwargs)
forms.py
def _guest_admin_form_factory(request):
class GuestAdminForm(forms.ModelForm):
class Meta:
model = Guest
exclude = []
def clean_user(self):
user = self.cleaned_data.get('user', None)
if not user:
return user
if user != request.user:
raise forms.ValidationError('Invalid request.')
return user
return GuestAdminForm
It occurred to me that Django might use the get_queryset() method to validate this for me, since some simple logging showed that the method is called twice when an object gets updated from the change form.
Is this the case, or do I need to stick to validating through a ModelAdmin form?
The documented way to do this is to define has_change_permission():
#admin.register(Guest)
class GuestAdmin(admin.ModelAdmin):
def get_queryset(self, request):
return super().get_queryset(request).filter(user=request.user)
def has_change_permission(self, request, obj=None):
return (obj is None or obj.user == request.user)
No need to muck about with the form.

Django form data lost on making login required on post

In my app, I want the following behaviour:
User goes to contact form url
User fills up the contact form
(a) If user is logged in, validate and submit the form
(b) If user is not logged in, redirect to login form for logging in and then if users credentials are validated, submit the form.
In my views.py, I have:
#method_decorator(login_required, name='post')
class ContactView(FormView):
template_name = 'extras/contact.html'
form_class = ContactForm
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
return self.form_valid(form, **kwargs)
else:
return self.form_invalid(form, **kwargs)
def form_valid(self, form):
instance = form.instance
instance.request_user = self.request.user
instance.save()
messages.success(self.request, "Thank you for contacting us! We will reach you soon.")
return super().form_valid(form)
def get_success_url(self):
return reverse('main_home')
The idea of including a post method was taken from this SO Answer
This does not work. The user is being redirected to login form, logged in, but displays a blank form and entered information before redirection is lost.
How can I resolve this problem, any hints?
When you redirect a post request to the login page, you’ll lose the post data. Therefore you should redirect GET requests as well, so that the user logs in before they fill out the contact form. You can do this by decorating the dispatch method.
#method_decorator(login_required, name='dispatch')
class ContactView(FormView):

Allow only the author of the post to edit in class based views- Django?

views.py
class EditPost(UserPassesTestMixin, LoginRequiredMixin, UpdateView):
model = Posts
form_class = PostForm
template_name="posts/add_post.html"
def test_func(self):
x = self.request.user.pk
print (x)
y = Posts.objects.get(user='user')
print (y)
if x == y:
return True
else:
if self.request.user.is_authenticated():
raise Http404("You are not allowed to edit this Post")
models.py
class Posts(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
post = models.CharField(max_length=1200, blank=False)
How do i match the loggedin user and the user object of the Post
i could not find any solution since i am using class based views.
try this:
add dispatch to EditPost class
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
if obj.user != self.request.user:
raise Http404("You are not allowed to edit this Post")
return super(EditPost, self).dispatch(request, *args, **kwargs)
Doing the check in test_func is tricky. You need to fetch the object once in test_func to check whether the user is allowed to use it, and then the object is fetched again by the same fiew.
An easier approach is to override the get_queryset method. If the user is not the author of the post, they will get a 404.
class EditPost(LoginRequiredMixin, UpdateView):
def get_queryset(self):
return super(EditPost, self).filter(user=self.request.user)
To add to the previous posts, try this:
Add to your class EditPost:
login_url = 'your url name or path to login page'
def test_func(self):
obj = self.get_object()
return obj.author == self.request.user
The test_func method is used by UserPassesTestMixin for this particular logic. To override it we set variable obj to the current object returned by the view using get.object().
After that, if the author on the current object (obj.author) matches the current user (self.request.user) we allow editing. If not (i.e. false) we throw an error.
login_url is from LoginRequiredMixin and the default location is /accounts/login. To override it, set your own path or name of the login template. This will take those people who are not logged in to the login page.

Categories