Django - CreateView passing the created objects id to another CreateView - python

I've been trying to pass the values of a newly created object to the next CreateView so that a new child model can be created.
This is what happens:
User creates the Parent model through a CreateView
If the form is valid the success_url redirects to the CreateView of another Child model. The child model in order to be created, needs the id of the Parent Model (ForeignKey relationship).
Once the Child Model is created redirect to a completed page.
Below I have an example of my code.
class AddParentModelView(LoginRequiredMixin, CreateView):
model = ParentModel
template_name = "dashboard/add_parent_model.html"
form_class = ParentModelForm
success_url = '/REDIRECT_TO_CHILD_MODEL/'
def form_valid(self, form):
form.instance.owner = self.request.user
# I Also tried sessions:
# self.request.session['parent_id'] = form.instance.id
# But they return None:
# print(self.request.session["venue_id"])
return super().form_valid(form)
class AddChildModelView(LoginRequiredMixin, CreateView):
model = ChildModel
template_name = "dashboard/add_child_model.html"
form_class = ChildModelForm
success_url = '/thanks/'
What's the proper way to approach this? If possible, please explain your solutions.
Thanks in advance!

Define get_success_url() on AddParentModelView to return the child URL including the ID of the parent.
For example, if AddChildModelView has a URL like:
path('/<int:parent_id>/add_child', AddChildModelView.as_view(), name='add-child')
then define the method like:
def get_success_url(self):
return reverse('add-child', kwargs={'parent_id': self.object.id})

Related

(Django CBV) Need object be attached to a user with CBV

I implemented this functionality with using FBV, but when I'm trying to use CBV, Objects were created with empty user field.
views.py
class BlockCreate(CreateView):
model = TrainingBlock
template_name = 'training_room/create_block.html'
form_class = BlockForm
success_url = reverse_lazy('gym')
def set_user(self, form):
form.instance.user = self.request.user
return super(BlockCreate, self).set_user(form)
models.py
class TrainingBlock(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
duration = models.IntegerField(default=10)
if_ended = models.BooleanField(default=False)
def __str__(self):
return self.name
forms.py
class BlockForm(forms.ModelForm):
class Meta:
model = TrainingBlock
fields = '__all__'
exclude = ['user']
There is no .set_user method in a CreateView, hence the logic will never get invoked. You use .form_valid(…) [Django-doc] instead:
from django.contrib.auth.mixins import LoginRequiredMixin
class BlockCreate(LoginRequiredMixin, CreateView):
model = TrainingBlock
template_name = 'training_room/create_block.html'
form_class = BlockForm
success_url = reverse_lazy('gym')
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
Note: You can limit views to a view to authenticated users with the
#login_required decorator [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.
Note: Since PEP-3135 [pep], you don't need to call super(…) with parameters if the first parameter is the class in which you define the method, and the second is the first parameter (usually self) of the function.

Django Class Based View (FormView)

class ReviewView(FormView):
form_class = ReviewForm
template_name = "review/review.html"
success_url = "/thank-you"
def form_valid(self, form):
form.save()
return super().form_valid(form)
this is the error
how can I fix it
AttributeError at /
'ReviewForm' object has no attribute 'save'
Forms don't have a save() method.
You need to use a ModelForm as that will then have a model associated with it and will know what to save where https://docs.djangoproject.com/en/dev/topics/forms/modelforms/
If you don't need to save anything that is fine.

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

How to set ForeignKey in CreateView?

I have a model:
class Article(models.Model):
text = models.CharField()
author = models.ForeignKey(User)
How do I write class-based view that creates a new model instance and sets author foreign key to request.user?
Update:
Solution moved to separate answer below.
I solved this by overriding form_valid method. Here is verbose style to clarify things:
class CreateArticle(CreateView):
model = Article
def form_valid(self, form):
article = form.save(commit=False)
article.author = self.request.user
#article.save() # This is redundant, see comments.
return super(CreateArticle, self).form_valid(form)
Yet we can make it short (thanks dowjones123), this case is mentioned in docs.:
class CreateArticle(CreateView):
model = Article
def form_valid(self, form):
form.instance.author = self.request.user
return super(CreateArticle, self).form_valid(form)
I just stumbled into this problem and this thread led me in the right direction (thank you!). Based on this Django documentation page, we can avoid calling the form's save() method at all:
class CreateArticle(LoginRequiredMixin, CreateView):
model = Article
def form_valid(self, form):
form.instance.author = self.request.user
return super(CreateArticle, self).form_valid(form)
Berislav's code in views.py doesn't work for me. The form is rendered as expected, with the user value in a hidden input, but the form is not saved (I don't know why). I have tried a slightly different approach, that works for me:
views.py
from django.views.generic import *
from myapp.forms import ArticleForm
from myapp.models import Article
class NewArticleView(CreateView):
model = Article
form_class = ArticleForm
def get_initial(self):
return {
"user": self.request.user
}
You should set up a CreateView using a ModelForm for that model. In the form definition, you set the ForeignKey to have the HiddenInput widget, and then use the get_form method on the view to set the value of your user:
forms.py:
from django import forms
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
widgets = {"user": forms.HiddenInput()}
views.py:
from django.views.generic import *
from myapp.forms import ArticleForm
from myapp.models import Article
class NewArticleView(CreateView):
model = Article
form_class = ArticleForm
def get_form(self, form_class):
initials = {
"user": self.request.user
}
form = form_class(initial=initials)
return form
There are answers that are mainly related to the User model foreign key. However, let's suppose a simple scenario in which there is a model Comment containing a foreign key of the Article model, and you need to have a CreateView for Comment where each comment will have a foreign key of the Article model. In that case, the Article id would probably be in the URL, for example, /article/<article-id>/comment/create/. Here is how you can deal with such a scenario
class CommentCreateView(CreateView):
model = Comment
# template_name, etc
def dispatch(self, request, *args, **kwargs):
self.article = get_object_or_404(Article, pk=self.kwargs['article_id'])
return super(CommentCreateView, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
form.instance.article= self.article # if the article is not a required field, otherwise you can use the commit=False way
return super(CommentCreateView, self).form_valid(form)

Categories