How to detailview pk in post method in DetailView? (Django) - python

In my detailView I have 2 methods get_context_data and post. In get_context_data I can get the detailView pk with self.object.pk but how can I get it in the post method?
[ updated ]
here is the view
class Class_detailView(LoginRequiredMixin, DetailView):
login_url = '/'
model = Class
template_name = "attendance/content/teacher/class_detail.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['attendance_form'] = AttendanceForm(current_class_pk=self.object.pk) # pass data to form via kwargs
return context
def post(self, request, *args, **kwargs):
if request.method == "POST":
attendance_form = AttendanceForm(request.POST)
if attendance_form.is_valid():
attendance_form.instance.teacher = self.request.user
attendance_form.save()
return redirect('class-detail', pk=self.kwargs.get('pk'))
form
class AttendanceForm(forms.ModelForm):
class Meta:
model = Attendance
fields = ['student',]
def __init__(self, *args, **kwargs):
current_class_pk = kwargs.pop('current_class_pk')
super(AttendanceForm, self).__init__(*args, **kwargs)
current_student = Class.objects.get(id=current_class_pk)
self.fields['student'].queryset = current_student.student
I want to get the pk and pass it to the form when the post request is called.
How can I do it?

did you try this:
def post(self, request, *args, **kwargs):
if request.method == "POST":
attendance_form = AttendanceForm(request.POST, current_class_pk=self.kwargs.get('pk'))
if attendance_form.is_valid():
attendance_form.instance.teacher = self.request.user
attendance_form.save()
return redirect('class-detail', pk=self.kwargs.get('pk'))

Related

Django: Different views for POST and GET - Form validation errors

I have a DemandDetailView(DetailView) and BidCreationView(CreateView).
On DemandDetailView page, there is a form (for creating Bids) which posts data to BidCreationView.
I can't figure out what to do in case form is invalid. I would like to render DemandDetailView again with form errors and preserve corresponding URL.
class DemandDetailView(DetailView):
model = Demand
template_name = 'demands/detail.html'
def dispatch(self, request, *args, **kwargs):
self.bid_creation_form = BidCreationForm(request.POST or None, request.FILES or None,request=request)
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['bid_creation_form']=self.bid_creation_form
return context
class BidCreationView(CreateView):
http_method_names = ['post']
model = Bid
form_class = BidCreationForm
def get_success_url(self):
return reverse_lazy("demands:demands")
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({'request': self.request})
return kwargs
def form_valid(self, form):
form.instance.demand_id = self.kwargs.pop('demand_id')
return super().form_valid(form)
Do you have any ideas? My only idea is to use session which isn't probably the best way.
You could use is_valid() method from Form Objects. Something like:
class DemandDetailView(DetailView):
model = Demand
template_name = 'demands/detail.html'
def dispatch(self, request, *args, **kwargs):
form = BidCreationForm(request.POST or None, request.FILES or None,request=request)
if form.is_valid():
self.bid_creation_form = form
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['bid_creation_form']=self.bid_creation_form
return context
Option #2 (Personal Choice):
forms.py
from django import forms
from .models import Bid
class BidCreationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(BidCreationForm, self).__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs = {"class": "form-control"}
class Meta:
model = Bid
fields = ('user', 'demands', 'amount', 'transaction')
Take a look at the Meta class within the Form. Its explicitly calling the Bid Model and the fields attribute is referring Bid fields from the Model Instance. Now you could call this form in any view without calling another view. If you want to add logic to this form, like calculating total amount or something like that then should do it within the form also. Write code once, Dont Repeat yourself.

Python edit form data prior to django validation

I'm fairly new to python and have been searching for awhile to find how I can edit form data BEFORE all the standard python form/field validators do their magic.
I have a model form with an IntegerField which I'd like to remove the "$" and commas from (using some sort of custom validation), then let the normal to_python() validate() etc do their thing.
My code is below - any help would be much appreciated!
forms.py
class BuyerSettingsForm(forms.ModelForm):
total_offer_limit = forms.IntegerField(required=False, max_value=10000000, min_value=0)
def __init__(self, request, *args, **kwargs):
super(BuyerSettingsForm, self).__init__(*args, **kwargs)
class Meta:
model = Buyer
fields = ['total_offer_limit']
def save(self, commit=True):
profile = super(BuyerSettingsForm, self).save(commit=commit)
profile.total_offer_limit = self.cleaned_data['total_offer_limit']
profile.save()
return profile
views.py
class SettingsPreferences(LoginRequiredMixin, BuyerAccessRequiredMixin, BuyerAdminAccessRequiredMixin, UpdateView):
template_name = 'invoicely/buyer/settings/buyer_settings.html'
form_class = BuyerSettingsForm
success_url = reverse_lazy('settings_preferences')
def get_object(self, *args, **kwargs):
return self.request.user.profile.buyer
def get_initial(self):
ctx = super(SettingsPreferences, self).get_initial()
ctx.update({
'total_offer_limit': self.object.total_offer_limit,
})
return ctx
def get_form_kwargs(self):
kwargs = super(SettingsPreferences, self).get_form_kwargs()
kwargs['request'] = self.request
return kwargs
def form_valid(self, form):
self.object = form.save()
messages.add_message(self.request, messages.SUCCESS, "Settings successfully updated")
return super(SettingsPreferences, self).form_valid(form)
If you are already overloaded get_form_kwargs you can do this. This is data which your form will be initialized with. So we can edit it before its initialization.
class SettingsPreferences(LoginRequiredMixin, BuyerAccessRequiredMixin, BuyerAdminAccessRequiredMixin, UpdateView):
...
def get_form_kwargs(self):
kwargs = super(SettingsPreferences, self).get_form_kwargs()
kwargs = copy.deepcopy(kwargs)
kwargs['request'] = self.request
if self.request.method in ('POST', 'PUT'):
# here put your data editing code
kwargs['data']['total_offer_limit'] = int(kwargs['data']['total_offer_limit'].strip().replace('$', ''))
return kwargs

Django 1.9 FormView never reach get_context_data

I have a FormView called LeagueTransferView based on a form LeagueTransferForm.
I'm trying to override get_context_data to add extra players to render in the template.
But get_context_data is never reached. It's working fine on other views like, DetailView, ListView,...
I'm missing something?
Below my configuration
View
class LeagueTransferView(FormView):
template_name = 'hockey/league/transfer_market.html'
form_class = LeagueTransferForm
success_url = ''
def get_context_data(self, **kwargs):
print('----NEVER REACHED----')
context = super(LeagueTransferView, self).get_context_data(**kwargs)
petitioner = get_object_or_404(Team, user=self.request.user.profile, league=self.kwargs['pk'])
context['players'] = Player.objects.filter(leagues=self.kwargs['pk']).exclude(teams=petitioner)
return context
def get(self, request, *args, **kwargs):
petitioner = get_object_or_404(Team, user=self.request.user.profile, league=self.kwargs['pk'])
form = self.form_class(initial={'league': self.kwargs['pk'], 'petitioner': petitioner})
form.fields['offered_player'].queryset = petitioner.players
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
transfer = form.save(commit=False)
team = Team.objects.filter(league=transfer.league, players__in=[transfer.requested_player])
if not team: # free agent
transfer.status = 1
messages.success(request, _('transfer succeeded'))
else:
print(team)
transfer.player_owner = team[0]
if transfer.petitioner.user is None: # bot team
transfer.status = 1
messages.success(request, _('transfer succeeded'))
else:
messages.success(request, _('transfer waiting for confirmation by player owner'))
transfer.save()
return HttpResponseRedirect(reverse('hockey_dashboard'))
petitioner = get_object_or_404(Team, user=self.request.user.profile, league=self.kwargs['pk'])
form.fields['offered_player'].queryset = petitioner.players
return render(request, self.template_name, {'form': form})
FORM
class LeagueTransferForm(forms.ModelForm):
class Meta:
model = Transfer
fields = ['league', 'requested_player', 'offered_player', 'player_owner', 'petitioner']
labels = {
'requested_player': _('Requested player'),
'offered_player': _('Offered player'),
}
widgets = {
'requested_player': forms.HiddenInput,
'league': forms.HiddenInput,
'player_owner': forms.HiddenInput,
'petitioner': forms.HiddenInput
}
Your code is never reaching get_context_data() because you have overridden the get() method and not calling the get_context_data() function there. You need to manually call the get_context_data() function at the time of passing context to render() in your code.
Instead of doing that, i would suggest you to try the below approach where instead of overrriding get() and returning your custom response, you only override what is necessary and let Django handle the rest.
class LeagueTransferView(FormView):
template_name = 'hockey/league/transfer_market.html'
form_class = LeagueTransferForm
success_url = ''
def get_context_data(self, **kwargs):
context = super(LeagueTransferView, self).get_context_data(**kwargs)
context['players'] = Player.objects.filter(leagues=self.kwargs['pk']).exclude(teams=self.petitioner)
return context
def get_initial(self):
initial = super(LeagueTransferView, self).get_initial()
initial['league'] = self.kwargs['pk'] # add custom data to initial
initial['petitioner'] = self.petitioner # add custom data to initial
return initial
def get_form(self, form_class=None):
form = super(LeagueTransferView, self).get_form(form_class)
# override the queryset
form.fields['offered_player'].queryset = self.petitioner.players
return form
def get(self, request, *args, **kwargs):
# only perform 1 query to get 'petitioner'
self.petitioner = get_object_or_404(Team, user=self.request.user.profile, league=self.kwargs['pk'])
return super(LeagueTransferView, self).get(request, *args, **kwargs)

Django ModelForm within DetailView

I have a DetailView which displays a Post. I now want to add the ability to create a Comment for a Post. For that I need a CommentForm, within the DetailView, so that I can create comments while being on the same page with a Post.
Is this possible, or should I be looking for another approach, like doing the form handling 'manually'?
class Comment(models.Model):
body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
author_name = models.CharField(max_length=255)
parent_post = models.ForeignKey('Post',related_name='comments')
class PostDetailView(BlogMixin,DetailView):
""" A view for displaying a single post """
template_name = 'post.html'
model = Post
#Add some code for the CommentForm here?
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
exclude = ("parent_post","created_at")
def create_view(request, **kwargs):
if request.method == "POST":
parent_fk = request.args['parent_fk'] #Im hoping to find how this will work soon
form = CommentForm(request.POST)
if form.is_valid():
new_comment = form.save(commit=False)
new_comment.parent_post = parent_fk
new_comment.save()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
** Alternative **
I have been trying to apply the solution - A better alternative - but I get
Exception Value: __init__() takes exactly 1 argument (3 given)
Exception Location: .../sitepackages/django/core/handlers/base.py in get_response, line 112
and have not been able to trace it yet.
class PostView(BlogMixin,DetailView):
""" A view for displaying a single post """
template_name = 'post.html'
model = Post
def get_context_data(self, **kwargs):
context = super(PostView, self).get_context_data(**kwargs)
context['form'] = CommentForm()
return context
class PostDetailView(View):
def get(self, request, *args, **kwargs):
view = PostView.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = PostComment.as_view()
return view(request, *args, **kwargs)
class PostComment( SingleObjectMixin , FormView):
template_name = 'post.html'
form_class = CommentForm
model = Post
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super(PostComment, self).post(request, *args, **kwargs)
def get_success_url(self):
return reverse('post-detail', kwargs={'pk': self.object.pk})
class BlogMixin(object):
"""
Basic mixin for all the views. Update the context with additional
information that is required across the whole site, typically
to render base.html properly
"""
def get_context_data(self, *args, **kwargs):
context = super(BlogMixin, self).get_context_data(*args, **kwargs)
blog = Blog.get_unique()
context.update({
'blog': blog,
'active_user': users.get_current_user(),
'is_admin': users.is_current_user_admin()
})
return context
urls.py:
url(r'^post/(?P[\d]+)/$', views.PostDetailView., name="post-detail"),
If you want to use your first method, you can make the FK a hidden field. In your view, you can save the FK before committing the comment to the database. Like this:
if form.is_valid():
comment = form.save(commit=False)
comment.parent_post = parent_post
comment.save()
Edit: If you want to fetch the comments, then you can use filter by post to get a QuerySet of the comments.
Why dont you send your form in the context in detail view:
class YourDetailView(DetailView):
#Your stuff here
def get_context_date(self, **kwargs):
context = super(YOurDetailView, self).get_context_data(**kwargs)
context['form'] = YourForm
return context
PS. Look for the parameters in get_context_date..
In the end I could not make it work with the redirect, but the following is working:
class PostDetailView(BlogMixin,CreateView):
""" A view for displaying a single post """
template_name = 'post.html'
model = Comment
fields = ['body','author_name']
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['post'] = Post.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
# self.object = form.save()
obj = form.save(commit=False)
obj.parent_post = Post.objects.get(pk=self.kwargs['pk'])
obj.save()
return redirect('post-detail', self.kwargs['pk'])

Django, change username form

I'm trying to create a modelform to allow users to change their username. I'm trying to do this using FormView instead of UpdateView because I want to include other forms (user management functionality) in this view eventually.
Relevant forms.py:
class FormHorizontalModelForm(forms.ModelForm)
def __init__(self, *args, **kwargs):
super(FormHorizontalModelForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
class PlayerRenameForm(FormHorizontalModelForm):
def __init__(self, user, *args, **kwargs):
super(PlayerRenameForm, self).__init__(*args, **kwargs)
self.helper.add_input(Submit('change_username', 'Change Username'))
class Meta:
model = User
fields = ('username',)
def save(self):
pass # I figured this might be saving the object since this is inherited off ModelForm
def form_valid(self, form):
self.change_username(new_username = self.cleaned_data['username'])
# self.send_email(new_username = self.cleaned_data['username'])
def change_username(self, new_username):
player = Player.objects.get(user = self.instance)
self.instance.username = new_username
self.instance.save()
player.changed_username = True
player.save()
views.py
class AccountView(UserAuthenticationMixin, FormView):
template_name = 'game/profile.html'
success_url = '/accounts/'
form_class = PlayerRenameForm
form_class_two = CrispyPasswordSetForm
form_class_three = CrispyPasswordChangeForm
def get_context_data(self, **kwargs):
context = super(AccountView, self).get_context_data(**kwargs)
if 'form' not in context:
context['form'] = self.form_class(user = self.request.user, data = (self.request.POST or None))
if 'password_set_form' not in context:
context['password_set_form'] = self.form_class_two(user = self.request.user, data = (self.request.POST or None))
if 'password_change_form' not in context:
context['password_change_form'] = self.form_class_three(user = self.request.user, data = (self.request.POST or None))
return context
def form_invalid(self, **kwargs):
return self.render_to_response(self.get_context_data(**kwargs))
def get_form(self, form_class):
return form_class(user = self.request.user, **self.get_form_kwargs())
def post(self, request, *args, **kwargs):
if 'change_username' in request.POST:
form = self.form_class(user = request.user, instance = request.user, data = self.request.POST)
form_name = 'form'
elif 'set_password' in request.POST:
form = self.form_class_two(user = request.user, data = self.request.POST)
form_name = 'password_set_form'
elif 'change_password' in request.POST:
form = self.form_class_three(user = request.user, data = self.request.POST)
form_name = 'password_change_form'
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(**{form_name: form})
The form returns valid, and returns to success_url. I know for a fact that it is passing into the correct part of the post function since I have stripped away the other parts to ensure that was working.

Categories