UpdateView define primary key - python

I am trying to set my primary key in a class based view to a unique value from my models.
models.py
from django.db import models
from django.forms import model_to_dict
class Stuff(models.Model):
thing = models.CharField(max_length=100, verbose_name="Thing", unique=True)
item = models.CharField(max_length=100, verbose_name="Item")
def __str__(self):
return self.thing
def toJSON(self):
item = model_to_dict(self)
return item
views.py
from django.urls import reverse_lazy
from django.views.generic import CreateView, UpdateView
class NewStuff(CreateView):
model = Stuff
form_class = NewStuffForm
template_name = 'stuff.html'
success_url = reverse_lazy('search_stuff')
def post(self, request, *args, **kwargs):
data = {}
try:
action = request.POST['action']
if action == 'add':
form = self.get_form()
data = form.save()
else:
data['error'] = "No option has been selected"
except Exception as e:
data['error'] = str(e)
return JsonResponse(data)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'New Stuff'
context['stuff_url'] = reverse_lazy('search_stuff')
context['action'] = 'add'
return context
class EditStuff(UpdateView):
model = Stuff
form_class = NewStuffForm
template_name = 'stuff.html'
success_url = reverse_lazy('search_stuff')
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
return super().dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
data = {}
try:
action = request.POST['action']
if action == 'edit':
form = self.get_form()
data = form.save()
else:
data['error'] = '"No option has been selected"
except Exception as e:
data['error'] = str(e)
return JsonResponse(data)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'New Stuff'
context['stuff_url'] = reverse_lazy('search_stuff')
context['action'] = 'edit'
return context
forms.py
from django import forms
from .models import Stuff
class NewStuffForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for form in self.visible_fields():
form.field.widget.attrs['class'] = 'form-control'
class Meta:
model = Stuff
fields = '__all__'
def save(self, commit=True):
data = {}
form = super()
try:
if form.is_valid():
form.save()
else:
data['error'] = form.errors
except Exception as e:
data['error'] = str(e)
return data
urls.py
from django.urls import path
from .views import *
urlpatterns = [
path('stuff/add/', NewStuff.as_view(), name='new_stuff'),
path('stuff/edit/<str:pk>', EditStuff.as_view(), name='edit_stuff'),
]
How can I set the pk on the view "EditStuff" equal to thing from the "Stuff" model?
I understand that I can get the primary key with self.kwargs['pk']. However, I do not know how to set it equal to the primary key...
My best guess is that I should do it inside the dispatch method, nevertheless, I am not entirely sure about it.

First, set slug_field and slug_url_kwarg attributes in your view as
class EditStuff(UpdateView):
slug_field = "thing"
slug_url_kwarg = "thing"
# rest of your code
Then, update the URL as
urlpatterns = [
path('stuff/add/', NewStuff.as_view(), name='new_stuff'),
path('stuff/edit/<str:thing>', EditStuff.as_view(), name='edit_stuff'),
]

Related

Pass kwarg into an inlineformset_factory?

I am trying to pass the request object into my inlineformset_factory and am struggling to accomplish this.
In forms.py I have the following:
class SummativeScoreForm(forms.ModelForm):
"""
Form definition for SummativeScore Form
"""
subdomain_proficiency_level = forms.ModelChoiceField(
empty_label="Undecided",
queryset=SubdomainProficiencyLevel.objects.none(),
widget=forms.RadioSelect,
required=False,
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request", None)
super(SummativeScoreForm, self).__init__(*args, **kwargs)
if self.instance:
if self.request.user == self.instance.summative.employee:
self.fields["subdomain_proficiency_level"].disabled = True
self.fields[
"subdomain_proficiency_level"
].queryset = SubdomainProficiencyLevel.objects.filter(
subdomain=self.instance.subdomain
)
self.fields[
"subdomain_proficiency_level"
].label = f"""
{self.instance.subdomain.character_code}:
{self.instance.subdomain.short_description}
"""
class Meta:
model = SummativeScore
fields = "__all__"
widgets = {
"subdomain_proficiency_level": forms.RadioSelect(
attrs={"class": "list-unstyled"}
),
}
SummativeScoreInlineFormset = inlineformset_factory(
Summative,
SummativeScore,
fields=("subdomain_proficiency_level",),
can_delete=False,
extra=0,
form=SummativeScoreForm,
)
I'm using a FormView CBV to show this inline_formset
class SummativeScoreFormView(
LoginRequiredMixin,
UserIsObserverOrObserveeMixin,
SingleObjectMixin,
FormView,
):
model = Summative
template_name = "commonground/summative_score_form.html"
pk_url_kwarg = "summative_id"
def get(self, request, *args, **kwargs):
summative_id = kwargs.pop("summative_id")
self.object = self.get_object(
queryset=Summative.objects.filter(id=summative_id)
)
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
summative_id = kwargs.pop("summative_id")
self.object = self.get_object(
queryset=Summative.objects.filter(id=summative_id)
)
return super().get(request, *args, **kwargs)
def get_form(self, form_class=None):
return SummativeScoreInlineFormset(
**self.get_form_kwargs(), instance=self.object
)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["request"] = self.request
return kwargs
def form_valid(self, form):
form.save()
messages.add_message(messages.SUCCESS, "Changes were saved!")
HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form):
print("invalid form")
return super().form_invalid(form)
def get_success_url(self):
user_id = self.kwargs["user_id"]
summative_id = self.kwargs["summative_id"]
return reverse(
"commonground:summative_detail",
kwargs={
"user_id": user_id,
"summative_id": summative_id,
},
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
summative = get_object_or_404(
Summative, pk=self.kwargs["summative_id"]
)
context["summative"] = summative
return context
I keep getting this error:
__init__() got an unexpected keyword argument 'request'
I'm not sure how best to resolve this - any ideas? Is there a clear way to pass the request to the inlineformset_factory?
You are passing the request to the formset and not to the forms in the formset, hence you get the error. For passing custom parameters to the formsets forms [Django docs] you need to pass a form_kwargs parameter to the formset:
class SummativeScoreFormView(
LoginRequiredMixin,
UserIsObserverOrObserveeMixin,
SingleObjectMixin,
FormView,
):
...
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['form_kwargs'] = {'request': self.request}
return kwargs

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

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'))

Unable to Update InlineFormset in Django with CBV

class PreChildrenView(CreateView):
model = PreDealDetails2
template_name = 'cam_app/children_form.html'
fields = '__all__'
success_url = reverse_lazy('forms_app:deal-entering')
session_initial = 'children_'
def get_initial(self,**kwargs):
initial = super(PreChildrenView, self).get_initial(**kwargs)
initial['deal_id'] = self.request.session['deal_id']
return initial
def get_context_data(self, **kwargs):
data = super(PreChildrenView, self).get_context_data(**kwargs)
if self.request.POST:
data['childrens'] = ChildrenFormSet(self.request.POST)
print('post')
else:
print('get')
data['childrens'] = ChildrenFormSet()
data['childrens'].extra = 5
data['info'] = 'Children Details'
return data
def form_valid(self, form):
print('wwwww')
context = self.get_context_data()
childrens = context['childrens']
if form.is_valid():
pass
if childrens.is_valid():
count = 0
self.object = form.save()
childrens.instance = self.object
childrens.save()
self.request.session[self.session_initial + 'children_count'] = count
self.request.session['valid_children'] = True
messages.success(self.request, 'Successfully filled Children Details')
return self.render_to_response(self.get_context_data(form=form))
else:
return super(PreChildrenView, self).form_invalid(form)
class UpdatePreChildrenView(UpdateView):
model = PreDealDetails2
template_name = 'cam_app/children_form.html'
fields = '__all__'
success_url = reverse_lazy('forms_app:deal-entering')
session_initial = 'children_'
def get_object(self, queryset=None):
return PreDealDetails2(deal_id = self.request.session['deal_id'])
def get_context_data(self, **kwargs):
data = super(UpdatePreChildrenView, self).get_context_data(**kwargs)
if self.request.POST:
a = PreDealDetails2.objects.get(deal_id = self.request.session['deal_id'])
data['childrens'] = ChildrenFormSet(self.request.POST)
print('post')
else:
print('get')
data['childrens'] = ChildrenFormSet(instance=self.object)
data['childrens'].extra = 5
data['info'] = 'Children Details'
return data
def form_valid(self, form):
print('update valid')
context = self.get_context_data()
childrens = context['childrens']
if form.is_valid():
print('wejri')
self.object =form.save()
if childrens.is_valid():
childrens.instance = self.object
childrens.save()
count = 0
self.request.session[self.session_initial + 'children_count'] = count
self.request.session['valid_children'] = True
messages.success(self.request, 'Successfully filled Children Details')
return self.render_to_response(self.get_context_data(form=form))
else:
return super(UpdatePreChildrenView, self).form_invalid(form)
else:
print('sfeief')
class PreChildrenRedirectView(RedirectView):
def get_redirect_url(self):
flag = 0
try:
PreDealDetails2.objects.get(deal_id=self.request.session['deal_id'])
flag = 1
except:
pass
if flag == 1:
return reverse("cam_app:update-prechildren-view")
else:
return reverse("cam_app:create-prechildren-view")
I am using above code to enter details to form and later update form.
CreateView is working fine and saving data.
UpdateView is displaying data. When i try to save updateview it is showing following error :-
Code Flow :-
First PreChildrenRedirectView is called. It checks if data already exist or not. If it does then Updateview is called and if it doesnot exist then Createview is called.
I am not using slug. Instead i am using get_object in Updateview. Let me know what is causing problem or how to solve the error.
Update
I have changed the forms.py to remove the error in child. But Deal Id is still showing error. I am also attaching the forms.py below
class ChildrenForm(forms.ModelForm):
class Meta:
model = PreChildrenDetails
fields = '__all__'
def __init__(self, *args, **kwargs):
super(ChildrenForm, self).__init__(*args, **kwargs)
self.fields['deal_id'].widget.attrs['readonly'] = True
self.helper = FormHelper(self)
self.helper.form_show_labels = False
def clean(self):
print('wow')
pass
ChildrenFormSet = inlineformset_factory(PreDealDetails2,PreChildrenDetails, form=ChildrenForm, fields = '__all__', extra=1)
Below is screenshot of error after updating

Blog matching query does not exist. (%3Fid=)

Used reverse() in the model's method get_absolute_url(), but django raises an error - DoesNotExist at /blog/detail/?id=23, becouse return not correct url - "/%3Fid=23" and urlpatterns haven't this in patterns, when I need the correct url - "/?id=23". Why this happens and how to fix it?
urls.py (blog app)
from django.conf.urls import url
from django.contrib.auth.decorators import permission_required
from blog.views import BlogListView, BlogDetailView, BlogCreate, BlogUpdate, BlogDelete
urlpatterns = [
url(r'^$', BlogListView.as_view(), name = "blog"),
url(r'^detail/(?:\?id=(?P<blog_id>\d+))?$', BlogDetailView.as_view(), name = "blog_detail"),
url(r'^add/(?:\?id=(?P<blog_id>\d+))?$', permission_required("blog.add_blog")(BlogCreate.as_view()), name = "blog_add"),
url(r'^edit/(?:\?id=(?P<blog_id>\d+))?$', permission_required("blog.change_blog")(BlogUpdate.as_view()), name = "blog_edit"),
url(r'^delete/(?:\?id=(?P<blog_id>\d+))?$', permission_required("blog.delete_blog")(BlogDelete.as_view()), name = "blog_delete"),
]
models.py (blog app)
from django.db import models
from datetime import datetime
from taggit.managers import TaggableManager
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django_comments.moderation import CommentModerator, moderator
from django.shortcuts import redirect
# Create your models here.
class Blog(models.Model):
title = models.CharField(max_length = 100, unique_for_date = "posted", verbose_name = "Title")
description = models.TextField(verbose_name = "Description")
content = models.TextField(verbose_name = "Content")
posted = models.DateTimeField(default = datetime.now(), db_index = True, verbose_name = "Posted")
is_commentable = models.BooleanField(default = True, verbose_name = "Comments are allowed")
tags = TaggableManager(blank = True, verbose_name = "Tags")
user = models.ForeignKey(User, editable = False)
def get_absolute_url(self):
return reverse("blog_detail", kwargs = {"blog_id": self.pk})
class Meta:
ordering = ["-posted"]
verbose_name = "blog article"
verbose_name_plural = "blog articles"
views.py (blog app/only BlogCreate, BlogDetailView + e.t.c)
...
class PageNumberView(View):
def get(self, request, *args, **kwargs):
try:
self.sort = request.GET.get("sort")
except KeyError:
self.sort = "0"
try:
self.order = request.GET.get("order")
except KeyError:
self.order = "A"
try:
self.search = self.request.GET.get("search")
except KeyError:
self.search = ""
try:
self.tag = self.request.GET.get("tag")
except KeyError:
self.tag = ""
return super(PageNumberView, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
try:
pn = self.request.GET.get("page", default=None)
if pn == None:
pn = "1"
except KeyError:
pn = "1"
self.success_url = self.success_url + "?page=" + pn
try:
self.success_url = self.success_url + "&search=" + self.request.GET.get("search")
except KeyError:
pass
try:
self.success_url = self.success_url + "&tag=" + self.request.GET.get("tag")
except KeyError:
pass
return super(PageNumberView, self).post(request, *args, **kwargs)
class PageNumberMixin(CategoryListMixin):
def get_context_data(self, **kwargs):
context = super(PageNumberMixin, self).get_context_data(**kwargs)
try:
context["pn"] = self.request.GET.get("page", default=None)
if context["pn"] == None:
context["pn"] = "1"
except KeyError:
context["pn"] = "1"
return context
class BlogDetailView(PageNumberView, DetailView, SearchMixin, PageNumberMixin):
model = Blog
template_name = "blog_detail.html"
def get_object(self):
return self.model.objects.get(pk=self.request.GET.get('id'))
class CategoryListMixin(ContextMixin):
def get_context_data(self, **kwargs):
context = super(CategoryListMixin, self).get_context_data(**kwargs)
context["current_url"] = self.request.path
context["categories"] = Category.objects.all()
return context
class BlogCreate(SuccessMessageMixin, CreateView, CategoryListMixin):
model = Blog
template_name = "blog_add.html"
success_message = "Article successfully created"
fields = '__all__'
def form_valid(self, form):
form.instance.user = self.request.user
return super(BlogCreate, self).form_valid(form)
...
You just write url like this:
url(r'^detail/(?P<blog_id>\d+)/$', BlogDetailView.as_view(), name = "blog_detail"),
Your url pattern definition should be
url(r'^detail/$', BlogDetailView.as_view(), name="blog_detail"),
if you want to use GET parameters (query parameters) as in /blog/detail/?blog_id=123. The parameter can be accessed in the view with
blog_id = request.GET.get('blog_id')
blog = get_object_or_404(Blog, id=blog_id)
something like that.
Or if you want to make the blog_id part of your URL, the URL pattern must be
url(r'^detail/(?P<blog_id>\d+)/$', BlogDetailView.as_view(), name="blog_detail"),
In which case your URL will look like /blog/detail/123/ and the value is available for access as a keyword argument.

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