Django UpdateView condition for form_valid() - python

So this is what I have:
#models.py
class Story(models.Model):
title = models.CharField(max_length=150)
story = models.TextField()
page = models.ForeignKey(Page, on_delete=models.CASCADE)
#views.py
class StoryUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Story
fields = ['title', 'content']
def form_valid(self, form):
story = self.get_object()
if self.request.user in User.objects.filter(groups__name=story.page.name):
return super().form_valid(form)
else:
pass
def test_func(self):
story = self.get_object()
if self.request.user in User.objects.filter(groups__name=story.page.name):
return True
return False
#urls.py
path('edit_story/<int:pk>', StoryUpdateView.as_view(), name='update-story')
here I want to give access to this update view to a group of users. So my query should be like this: if the current user is in User.objects.filter(groups__name=story.page.name) group, then he should have access to update a story.
Now I believe that my form_valid() and test_func() method is wrong. But I can't find a way to make it right. What should be the right logic for doing this?
Also, to get the story, what should I do? Do I do story = self.get_object() as done here which possibly is not working or do I need to use the method get_object_or_404() and how to do that?
Any help will be much appreciated

Related

how can i post form object, correctly?

i am practicing CBV , so i thought to check if i can override methodes, well one of biggest problems is that idk how to use data(like data just submitted ), i wrote this code for a DetailView so i could see post and comments under it:
class ArtDetailView(FormView, DetailView):
model = Art
form_class = CommentForm
def get_context_data(self, **kwargs):
context = super(ArtDetailView, self).get_context_data(**kwargs)
context['time'] = timezone.now()
context['form'] = self.get_form()
return context
def form_valid(self, form):
form.instance.writer = self.request.user
form.instance.text = self.post
#form.instance.art = Art.objects.get(id=self.pk)
form.save()
return super().form_valid(form)
def get_success_url(self) -> str:
return reverse('pages:art_detail', args=(self.kwargs['pk'],))
forms.py:
from django import forms
from .models import Art, Comment
class CommentForm(forms.ModelForm):
class Meta():
model = Comment
fields = ['text','art']
but when i post something it is in this shape:screen_shot(2nd comment)
,i think problem is withform.instance.text = self.post but i don't know how to fix it
can you please also explain a little because all i want is to learn.
and i tried to also add art as autofill(i added as comment) but wasn't successful, can you pls check it it too.
You can overide post method to save form as well. Since you only adding form which is always a text and no possible errors in forms unless blank. You code snippet will look like this
# In forms.py
def CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('text', )
# In views.py
class ArtDetailView(FormView, DetailView):
# same as previous but remove form_valid method
def post(self, request, *args, **kwargs):
form = CommentForm(request.POST)
if form.is_valid():
form_instance = form.save(commit=False)
# this will give form instance and then add related field and save again (commit=True by default)
form_instance.writer = request.user
# since it is art detail view, self.get_object() will give art object
form_instance.art = self.get_object()
form_instance.save()
return HttpResponseRedirect(self.get_success_url())
else:
return super().form_invalid(from)
If you go to FormView declaration you will find like this

Disallow All Site Users From appearing In Post Form During Post Creation

The post rationale was to permit all clients on my site to have the option to make post and news which is a success however shockingly while making a post all the entire aunthenticated client/users in the data set shows up for the specific confirmed client to browse and this are data I don't need the client/users to see when making post and I have no arrangement to eliminate the users Foreignkey from the post Model since it encourages me actualizing other site basic capacities and the Author functions. since the users/client will fill in as creator/author
class Post(models.Model):
image = models.ImageField(upload_to="images/")
title = models.CharField(max_length=150)
summary = models.CharField(max_length=250)
category = models.ForeignKey(PostCategory, on_delete=models.CASCADE)
content = RichTextUploadingField()
date_posted = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE)
slug = models.SlugField(max_length=250, unique=True, blank = True)
def onlyselfuser(self, *args, **kwargs):
self.user = self.request.user
super().onlyselfuser(*args, **kwargs)
#please take a look at my view.py files
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = __all__
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
i will appreciate if someone can show me a way out where the login user( user=request.user) will only appear excluding other users on moder and i tried a OneToOneField still and it didnt work. i Don't want to see all site user in user but specific useri
You can do something like setting the fields you want to show:
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['image', 'title', ....] # Don't add user fields name here
def form_valid(self, form):
form.instance.user = self.request.user # You wrote instance.author but your model had it as user
return super().form_valid(form)
But this is very tedious. A better way would be to use a ModelForm and add the user field to exclude. Plus this allows you much more customization!:
from django.forms import ModelForm
class PostForm(ModelForm):
class Meta:
model = Post
exclude = ['user']
class PostCreateView(LoginRequiredMixin, CreateView):
form_class = PostForm
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)

Django set bookmark for url querystring

I'm querying through some filters (django-filters) a list of Book objects, what I want to do is save my research (aka my url with the GET params), creating a Bookmark object for the user.
models.py
class Bookmark(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
name = models.CharField(max_length=20)
url = models.URLField(max_length=200)
forms.py
class BookmarkForm(BootstrapForm, forms.ModelForm):
class Meta:
model = Bookmark
fields = ['nome', 'url']
widgets = {
'url': forms.HiddenInput()
}
To do so I'm extending a CreateView and trying to pass current url to the initial valued of bookmark.url.
class AddBookmarkView(PermissionRequiredMixin, LoginRequiredMixin, CreateView):
permission_required = 'core.add_bookmark'
template_name = 'core/bookmark_form.html'
model = Bookmark
form_class = BookmarkForm
def get_initial(self):
initial = super().get_initial()
initial['url'] = request.GET.urlencode()
return initial
def get_success_url(self):
return reverse('elenco_libri')
def form_valid(self, form):
bookmark = form.save()
bookmark.user = self.request.user
bookmark.save()
return HttpResponseRedirect(self.get_success_url())
This doesn't get anywhere because I don't have the GET request in get_initial() function. Where could I store the url? I thought about the get() function but I don't know how. Can anyone help? Thanks in advance.
This doesn't get anywhere because I don't have the GET request in get_initial() function.
Actually you have, the request object in a class-based view is an attribute: self.request.
So you can generate a URL with:
class AddBookmarkView(PermissionRequiredMixin, LoginRequiredMixin, CreateView):
permission_required = 'core.add_bookmark'
template_name = 'core/bookmark_form.html'
model = Bookmark
form_class = BookmarkForm
def get_initial(self):
initial = super().get_initial()
initial['url'] = self.request.GET.urlencode()
return initial
def form_valid(self, form):
bookmark = form.save(commit=False)
bookmark.user = self.request.user
bookmark.save()
return HttpResponseRedirect(self.get_success_url())
The above will however, only give you the querystring. If you are interested in the path as well, you might want to use request.get_full_path() [Django-doc].
You probably also do not want to save the object to the database in the form_valid function, so first obtain the bookmark, then set the user, and then save that object to the database.
Note that a HiddenInput() is rendered at the client side, and thus can be tampered with. Furthermore I'm not convinced that setting a bookmark through a form is per se necessary. You could construct a view that for example sets a bookmark with an AJAX call, and render a form with JavaScript to set the name and submit the bookmark.

Django CBV Detailview

Hello Everybody excuse my english....
I am facing a problem with django.
I need to restrict object so only their owners can print it.
Model.py
class Post(models.Model):
title = models.CharField(max_length=50, blank=False)
prenom = models.CharField(max_length=255, blank=False)
user = models.ForeignKey(User, null=False)
View.py
class detailpost(DetailView):
model = Post
template_name = 'detail-post.html'
context_object_name = 'post'
url.py
url(r'detail-post/(?P<pk>[-\d]+)$', views.detailpost.as_view(), name='detailpost'),
This works properly but the problem is that every users can access to the post of another user (http://localhost:8000/detail-post/1). So my question is how can i do some stuff befor rendering the page and see if the post belongs to the current user if yes we print it else we redirect the user to another page.
You can use the LoginRequiredMixin (new in Django 1.9) to make sure that only logged in users can access the view.
Then override the get_queryset method, and filter the queryset so that it only includes posts by the logged-in user.
from django.contrib.auth.mixins import LoginRequiredMixin
class DetailPost(LoginRequiredMixin, DetailView):
model = Post
template_name = 'detail-post.html'
context_object_name = 'post'
def get_queryset(self):
queryset = super(DetailPost, self).get_queryset()
return queryset.filter(owner=self.request.user)
If the user views a post that does not belong to them, they will see a 404 page. If you must redirect the user instead of showing a 404, then you'll have to take a different approach.
Note that I have renamed your class DetailPost (CamelCase is recommended for classes in Django. You'll have to update your urls.py as well.
You can override get() or post() method in your view class.
from django.shortcuts import redirect
class detailpost(DetailView):
model = Post
template_name = 'detail-post.html'
context_object_name = 'post'
def get(self, request, *args, **kwargs):
self.post = Post.objects.get(pk=self.kwargs['pk'])
if self.post.user != request.user or not request.user.is_superuser:
return redirect('login')
else:
return super(detailpost, self).get(request, *args, **kwargs)
You should override 'get()' method in your 'detailpost' class, so that it would be something like below:
def get(self, request, *args, **kwargs):
queryset = self.model._default_manager.filter(user=request.user)
self.object = self.get_object(queryset)
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
It seems like it is the only way to pass User from Request instance to filter queryset.
I did not find that DetailView uses self.request

Django CreateView is not saving object

I'm practicing django Class-Based-View with a basic blog application.
For some reason, however, the CreateView for my Post model is not saving the post inside the database.
models.py
class Post(models.Model):
user = models.ForeignKey(User)
post_title = models.CharField(max_length=200)
post_content = models.CharField(max_length=500)
post_date = models.DateTimeField('date posted')
forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
exclude = ('user', 'post_date')
views.py
class PostCreate(CreateView):
template_name = 'app_blog/post_save_form.html'
model = Post
form_class = PostForm
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.now()
return super(PostCreate, self).form_valid(form)
It displays content without generating any error, but when I check the admin page,
the post created by the CreateView is not saved in the database..
Any idea..??
Thanks
One tip: don't use exclude when defining forms, use fields, is more secure and the recommended way to do it.
The redirect is defined by get_success_url method. If you have in your model the method get_absolute_url CreateView will redirect to that URL, otherwise you can always override get_success_url in your view.
Using get_absolute_url:
class Post(models.Model):
user = models.ForeignKey(User)
post_title = models.CharField(max_length=200)
post_content = models.CharField(max_length=500)
post_date = models.DateTimeField('date posted')
#permalink
def get_absolute_url(self):
return ('myurlname', (), {'myparam': something_useful})
Using get_success_url:
class PostCreate(CreateView):
template_name = 'app_blog/post_save_form.html'
model = Post
form_class = PostForm
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.now()
form.save()
return super(PostCreate, self).form_valid(form)
def get_success_url(self):
return reverse('myurlname', args=(somethinguseful,))
I think you will find this page very useful when working with CBVs:
http://ccbv.co.uk/projects/Django/1.5/django.views.generic.edit/CreateView/
the problem is that you are excluding fields that are mandatory, so it won't pass through your form validation.
You should pass this fields hidden with some default value, let the use fill them, set them to null=True or populate them before you access form_valid
I came across this question today after many years but those answer seems not correctly.
The main issue here is the form.instance is None for CreateView. So my approach is below as suggestion form django docs:
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
instance.post_date = datetime.now()
instance.save()
return redirect(self.get_success_url())
I think this is a simple case of not calling form.save(). When the form is validated, all of the checks are done, but it doesn't actually save the object in the database. To do that, you explicitly need to tell it to, via the save() method.
So you want:
class PostCreate(CreateView):
template_name = 'app_blog/post_save_form.html'
model = Post
form_class = PostForm
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.now()
form.save()
return super(PostCreate, self).form_valid(form)

Categories