I have a DetailView, and need add a form to user contact.
views.py
class ProductView(FormView, DetailView):
model = Product
template_name = 'product_detail.html'
form_class = NotificationForm
success_url = '/products/'
def post(self, request, *args, **kwargs):
return FormView.post(self, request, *args, **kwargs)
forms.py
class NotificationForm(ModelForm):
..."""some fields"""
class Meta:
model = Notification
fields = [
'usernameclient',
'emailclient',
'emailclient_confirmation',
'phoneclient',
'messageclient',
]
The model Notification is where is stored the data from that form
models.py
class Notification(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
..."""some fields"""
I dont understand yet how django workflow with forms. After the submition is correct rediretc to success_url, but nothing is save in db... What is missing?
A FormView does not save the form, a CreateView does. You can save the form with:
class ProductView(FormView, DetailView):
model = Product
template_name = 'product_detail.html'
form_class = NotificationForm
success_url = '/products/'
def form_valid(self, form):
form.save()
return super().form_valid(form)
Related
I'm a django newbie and i'm making a form where a User can make a Post and pick one of his Vehicles for the Post. The Vehicle and the Post models are created like so:
*blog/models.py*
class Post(models.Model):
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE, null=True)
def get_absolute_url(self):
return reverse('post-detail', kwargs ={'pk': self.pk} )
*vehicles/models.py*
class Vehicle(models.Model)*:
TESLA = 'TESLA'
MAZDA = 'MAZDA'
VOLVO = 'VOLVO'
VEHICLE_CHOICES = (
(TESLA, "Tesla"),
(MAZDA, "Mazda"),
(VOLVO, "Volvo"),
)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
model = models.CharField(max_length=9,
choices=VEHICLE_CHOICES,
default=TESLA)
def __str__(self):
return self.model
My blog views:
*blog/views.py*
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = [ 'vehicle']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
I would like to filter the vehicles so that only the current logged in User's vehicles
show up in the form, i've tried a variety of different solutions but I seem to be going around in circles, if you could help me out that would be awesome. Thanks!
Since you are using createview, you can create a form in forms.py. First you have to send the logged in user to the form, then in the form, pop the user from kwargs and use it to filter the vehicles.
views.py
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
form_class = PostForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['vehicle']
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
self.fields['vehicle'].queryset = Vehicle.objects.filter(owner=user)
I have two models as below
class Watched(Stamping):
user = models.ForeignKey("User", null=True, blank=True, on_delete=models.CASCADE,
default=None)
count = models.PositiveIntegerField()
class Link(Stamping):
...
user = models.ForeignKey(User, on_delete=models.CASCADE, default=None)
url = models.CharField(max_length=256, default=None)
watched = models.ForeignKey(Watched, null=True, blank=True, on_delete=models.CASCADE, default=None)
...
My forms.py
class SimpleLink(forms.Form):
url = forms.URLField(max_length=256)
A user can create a Link object and when some conditions are met, the object will be added to Watched. The Watched model contains objects created by different users.
Now I want to filter the Watched class and grab only the objects created by the requesting user in the Link model but I don't know how I can achieve that. Any help will be appreciated.
A sample of what I want to achieve is...
Watched.objects.filter(Link.objects.filter(user=request.user). I know my sample is crazy. But from the outside query, I want to grab the Link objects created by user making the request
You need to limit the queryset in your ModelForm. A ModelForm will thus look like:
from django import forms
class LinkForm(forms.ModelForm):
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
if user is not None:
self.fields['watched'].queryset = Watched.objects.filter(
link__user=user
)
class Meta:
model = Link
fields = ['url', 'watched']
In our view, we can then set the user object:
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
#login_required
def some_view(request):
if request.method == 'POST':
form = LinkForm(request.POST, user=request.user)
if form.is_valid():
form.instance.user = request.user
form.save()
return redirect('name-of-some-form')
else:
form = LinkForm(user=request.user)
return render(request, 'some-template.html', {'form': form})
For a class-based view, we can override the .get_form_kwargs(…) method [Djangod-doc]:
from django.contrib.auth.mixins import LoginRequiredMixin
class SomeView(LoginRequiredMixin, CreateView):
form_class = LinkForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
I'm trying to restrict access of CRUD pages to the owners, but I can't find the class-based view equivalent of "if request.user != post.author raise Http404". Thx for your time.
models.py
class Article(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('article_detail', args=[str(self.id)])
views.py
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
model = Article
fields = ['title', 'body']
template_name = 'article_edit.html'
login_url = 'login'
I tried the following (and many other combination arround those lines), but it isn't working.
def get(self, request, *args, **kwargs):
if self.request.user == self.obj.author:
raise Http404()
Youn can do something like this:-
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
model = Article
fields = ['title', 'body']
template_name = 'article_edit.html'
login_url = 'login'
def get(self, request, *args, **kwargs):
self.obj = self.get_object()
if self.request.user != self.obj.author:
raise Http404()
return super(ArticleUpdateView, self).get(request, *args, **kwargs)
I think you can override the get_queryset method to achieve this. For example:
class ArticleUpdateView(...):
def get_queryset(self):
queryset = super(ArticleUpdateView, self).get_queryset()
return queryset.filter(author = self.request.user)
So, when a user tries to update an post which is not created by him, then he will not be able to get it because will not be able find the post object in Queryset provided by get_queryset method. For details, please SingleObjectMixin which is later sub-classed by UpdateView. FYI you don't need to override the get method for this implementation.
I have DetaiView for my post and I want to use a form in this view so I decided to use DetailView with FormMixin. I need to set some initial to this form and I don't know how to do it. Here is my code:
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, related_name="comments", on_delete=models.CASCADE)
name = models.CharField("Nick", max_length=80)
email = models.EmailField()
body = models.TextField("Body")
created = models.DateTimeField("created", auto_now_add=True)
updated = models.DateTimeField("Updated", auto_now=True)
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = (
"name",
"email",
"body"
)
views.py
class PostDetailView(FormMixin, DetailView):
model = Post
form_class = CommentForm
template_name = "newspaper/post-detail.html"
def get_success_url(self):
return reverse("post-detail", kwargs={"slug": self.object.slug})
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context["form"] = self.get_form()
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
return super().form_valid(form)
So I want to post in CommentForm to post of this DetailView. I hope you understand :D.
Thanks in advance for the help!
With FormMixin you can specify form's initial using initial attribute:
class PostDetailView(FormMixin, DetailView):
model = Post
form_class = CommentForm
template_name = "newspaper/post-detail.html"
initial={'name': 'John'}
Or get_initial method:
def get_initial(self):
return {"post": self.get_object() }
In a class-base UpdateView in Django, I exclude the user field as it is internal to the system and I won't ask for it. Now what is the proper Django way of passing the user into the form.
(How I do it now, is I pass the user into the init of the form and then override the form's save() method. But I bet that there is a proper way of doing this. Something like a hidden field or things of that nature.
# models.py
class Entry(models.Model):
user = models.ForeignKey(
User,
related_name="%(class)s",
null=False
)
name = models.CharField(
blank=False,
max_length=58,
)
is_active = models.BooleanField(default=False)
class Meta:
ordering = ['name',]
def __unicode__(self):
return u'%s' % self.name
# forms.py
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
exclude = ('user',)
# views.py
class UpdateEntry(UpdateView):
model = Entry
form_class = EntryForm
template_name = "entry/entry_update.html"
success_url = reverse_lazy('entry_update')
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(UpdateEntry, self).dispatch(*args, **kwargs)
# urls.py
url(r'^entry/edit/(?P<pk>\d+)/$',
UpdateEntry.as_view(),
name='entry_update'
),
Hacking around like passing a hidden field doesn't make sense as this truly has nothing to do with the client - this classic "associate with logged in user" problem should definitely be handled on the server side.
I'd put this behavior in the form_valid method.
class MyUpdateView(UpdateView):
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
super(MyUpdateView, self).save(form)
# the default implementation of form_valid is...
# def form_valid(self, form):
# self.object = form.save()
# return HttpResponseRedirect(self.get_success_url())
Must return an HttpResponse object. The code below works:
class MyUpdateView(UpdateView):
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
return super(MyUpdateView, self).form_valid(form)
We can also do like
class MyUpdateView(UpdateView):
form_class = SomeModelForm
def form_valid(self, form):
form.instance.user = self.request.user
return super(MyUpdateView, self).form_valid(form)