I'm having trouble passing some additional context into a CBV. When I pass 'userprofile' as context, it prevents any other context from successfully being passed into the view.
My view started as this:
class OrderDetail(LoginRequiredMixin, DetailView):
model = Order
def dispatch(self, request, *args, **kwargs):
try:
user_checkout = UserCheckout.objects.get(user=self.request.user)
except:
user_checkout = None
if user_checkout:
obj = self.get_object()
if obj.user == user_checkout and user_checkout is not None: #checks to see if the user on the order instance ties to the user of the current request
return super(OrderDetail, self).dispatch(request, *args, **kwargs)
else:
raise Http404
else:
raise Http404
I then tried adding this
def get_context_data(self, *args, **kwargs):
context = super(OrderDetail, self).get_context_data(*args, **kwargs)
userprofile = UserProfile.objects.get(user=self.request.user)
context["userprofile"] = userprofile
I don't get any errors. It's just that when the page loads, none of the values that should appear (based on context) show up.
Thanks!
I think you need to add return context in your get_context_data method:
def get_context_data(self, *args, **kwargs):
context = super(OrderDetail, self).get_context_data(*args, **kwargs)
userprofile = UserProfile.objects.get(user=self.request.user)
context["userprofile"] = userprofile
return context
Also, as your userprofile has a relation(FK or OneToOne) with User model, you can simply access them template(without passing it in context) like this:
// If OneToOne
{{ user.userprofile }}
// If FK
{{ user.userprofile_set.first }} // using reverse relationship to fetch userprofiles
For more details, please check documentations on OneToOne, FK, Reverse Relationship.
Related
I am building a CRM where I want each client to have multiple plans, and each plan to have multiple notes. When a user creates a new note, I want them to be able to select a relevant plan from a dropdown of plans belonging to the client. From what I can find, I should be able to get the contact_id from the kwargs, but my errors show nothing in kwargs. I know there should be a way to do this, but I can't seem to find it.
Variable Value
__class__ <class 'lynx.forms.SipNoteForm'>
args ()
kwargs {}
self <SipNoteForm bound=False, valid=Unknown, fields=(sip_plan;note;note_date;fiscal_year;quarter;class_hours;instructor;clients)>
Views.py
#login_required
def add_sip_note(request, contact_id):
form = SipNoteForm()
if request.method == 'POST':
form = SipNoteForm(request.POST)
if form.is_valid():
form = form.save(commit=False)
form.contact_id = contact_id
form.user_id = request.user.id
form.save()
return HttpResponseRedirect(reverse('lynx:client', args=(contact_id,)))
return render(request, 'lynx/add_sip_note.html', {'form': form})
Forms.py
class SipNoteForm(forms.ModelForm):
class Meta:
model = SipNote
exclude = ('created', 'modified', 'user', 'contact')
def __init__(self, *args, **kwargs):
super(SipNoteForm, self).__init__(*args, **kwargs)
self.fields['sip_plan'].queryset = SipPlan.objects.filter(contact_id=kwargs.get("contact_id"))
Urls.py
path('add-sip-note/<int:contact_id>/', views.add_sip_note, name='add_sip_note'),
You are trying to get the kwargs in __init__(self, *args, **kwargs) as
def __init__(self, *args, **kwargs):
contact_id = kwargs.pop('contact_id')
super(SipNoteForm, self).__init__(*args, **kwargs)
self.fields['sip_plan'].queryset = SipPlan.objects.filter(contact_id=contact_id)
But you are not passing contact_id kwargs to the form while posting. you should pass kwargs to the form you are going to get in __init__(self, *args, **kwargs) such as
#login_required
def add_sip_note(request, contact_id):
form = SipNoteForm()
if request.method == 'POST':
form = SipNoteForm(request.POST, contact_id=contact_id)
I have a template view that is supposed to take in a user submitted search. I planning to use the get method to do a query in the get_context_data so that I can show some results on the HTML. Unfortunately, my get_context_data does not work while the get method and dispatch in my templateview works fine. The get_context_data does not run at all when the user submits the search.
class PollSearchView(TemplateView):
template_name = 'polls/polls_search.html'
def get(self, request, **kwargs):
self.request.session["search"] = request.GET.get("search")
return render(request, 'polls/polls_search.html')
def dispatch(self, *args, **kwargs):
dispatch = super(PollSearchView, self).dispatch(*args, **kwargs)
#exit if no search
if self.request.GET.get("search") == None:
pass
return redirect('/')
return dispatch
def get_context_data(self, **kwargs):
context = super(PollSearchView, self).get_context_data(**kwargs)
search = self.request.session.get("search")
context["test"] = search
return context
I have another class that is redirecting to the class above based a user input through a form.
class HomeView(TemplateView):
template_name = "home.html"
def get_context_data(self, *args, **kwargs):
context = super(HomeView, self).get_context_data(*args, **kwargs)
context["form"] = SearchForm()
return context
I think the form works completely fine, why the get_context_data does not take in any information baffles me, and I seek alternative ways to render the context based on my results from get. Any guidance on why this does not work and how to go about doing this will be great.
Thanks all
Instead of rendering the form using context["form"] = SearchForm(), include a formclass in the first templateview to render the form for the user.
class HomeView(TemplateView, FormView):
template_name = "home.html"
title = 'Your Dashboard'
form_class = SearchForm
on the second templateview, do a self.request.GET to collect the user input inside get_context_data method.
class PollSearchView(TemplateView):
template_name = 'polls/polls_search.html'
def get_context_data(self, **kwargs):
context = super(PollSearchView, self).get_context_data(**kwargs)
print self.request.GET
return context
This will enable the get_context_data to get the user input.
I am writing a FormView which will add for example a comment on Person object.
I want to check if current user wrote a comment for this Person and if he did I'd like to raise Http404.
Question: What is the best place for this validation? I don't want to call validation in get_context_data and form_valid. Is dispatch method a good place for this logic?
Remember that form_valid will only be called when you POST the form so that won't work as GET requests will still render. You could therefore put it in the get method for the FormView which would prevent the view and template loading the initial form. The drawback is that people could technically still POST to that URL if they really wanted to.
As you mentioned, I would put it in the dispatch method. It is very early in the cycle of the FormView so you avoid unnecessary processing.
def dispatch(self, request, *args, **kwargs):
# I'm just guessing your Comment/Person models
user = self.request.user
try:
person = Person.objects.get(user=self.request.user)
except:
raise Http404("No user exists")
if Comment.objects.filter(content_object=person).exist():
raise Http404("Comment already exists")
return super(MyFormView, self).dispatch(request, *args, **kwargs)
EDIT This is a good link describing the various ways you can decorate your class based view to add permission and user checking
I wrote this mixin
class PermissionCheckMixin(object):
def __init__(self, perm=None, obj=None):
self.perm = perm
self.obj = obj
def dispatch(self, request, *args, **kwargs):
if request.user.is_anonymous():
if request.is_ajax():
return JSONResponseForbidden()
else:
return HttpResponseForbidden()
elif request.user.is_authenticated():
if self.perm:
if request.user.has_perm(self.perm, self.obj):
return super(PermissionCheckMixin, self).dispatch(request, *args, **kwargs)
else:
if request.is_ajax():
return JSONResponseForbidden()
else:
return HttpResponseForbidden()
else:
if request.is_ajax():
return JSONResponseForbidden()
else:
return HttpResponseForbidden()
And use it like this:
class TestFormView(PermissionCheckMixin, FormView):
...
You can easily adapt this mixin, somehow like this:
def __init__(self, pk):
self.person_pk = pk
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated():
if request.user.pk == self.person_pk:
return HttpResponseNotFound()
In my forms.py I raise an validation error when the user is already a member of the project. If i try to add a user who is already a member the validation error gets perfectly raised, but then I get redirected to the template and I have no context any more.
Any Best Practices in raising a form validation error? What am I doing wrong?
class AddUserForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.project = kwargs.pop('project')
self.user = kwargs.pop('user')
super(AddUserForm, self).__init__(*args, **kwargs)
self._user_cache = None
def clean_user(self):
"""
Check if the user is already a member of the project.
"""
user = self.cleaned_data['user']
if ProjectMember.objects.filter(project=self.project, user=user).exists():
raise forms.ValidationError(_("User is already a member of this project."))
# store user instance we queried for here to prevent additional lookups.
self._user_cache = user
return user
views.py without the ProjectUpdate view because it does not matter in this case. The views are a little bit complicated, because I have 2 forms in one template. If you know any better way to accomplish this, let me know.
class ProjectDetailView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
view = ProjectDisplay.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
if 'update_form' in request.POST:
view = ProjectUpdate.as_view()
elif 'add_user_form' in request.POST:
view = ProjectAddUser.as_view()
return view(request, *args, **kwargs)
class ProjectDisplay(DetailView):
model = Project
def get_context_data(self, **kwargs):
context = super(ProjectDisplay, self).get_context_data(**kwargs)
context['update_form'] = ProjectUpdateForm(initial={
'name': self.object.name,
'description': self.object.description
})
context['add_user_form'] = AddUserForm(project=self.object, user=self.request.user)
context['project'] = self.object
context['is_member'] = self.object.user_is_member(self.request.user)
return context
class ProjectAddUser(CreateView):
model = ProjectMember
form_class = AddUserForm
template_name = 'projects/project_detail.html'
def get_success_url(self):
return reverse('project_detail', kwargs={'slug': self.get_object().slug})
def get_object(self, queryset=None):
return Project.objects.get(slug=self.kwargs['slug'])
def get_form_kwargs(self):
kwargs = super(ProjectAddUser, self).get_form_kwargs()
kwargs.update({'project': self.get_object()})
kwargs.update({'user': self.request.user})
return kwargs
I have a ModelForm in my application in which I want to modify init function to add some customisation.
When init is commented out then the form works and validates properly. When I override init and go to url where the form is rendered it automatically says that "Field xyz is required"
Whats the cause of that problem?
class CreateListView(FormMixin, ListView):
def get_context_data(self, **kwargs):
self.object_list = self.get_queryset()
data = super(ListView, self).get_context_data()
data['object_list'] = self.get_queryset()
data['form'] = self.get_form(self.form_class)
return data
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
form = form.save()
return HttpResponseRedirect(form.get_absolute_url())
return self.form_invalid(self.get_context_data())
class ActionGroupForm(forms.ModelForm):
class Meta:
model = ActionGroup
def __init__(self, *args, **kwargs):
super(ActionGroupForm, self).__init__(args, kwargs)
You are missing *, **:
super(ActionGroupForm, self).__init__(*args, **kwargs)