GenericViews - and pushing model name in URL - django

using Django Generic CreateView - is it possible for me to pass a value into the CreateView from the URL call, that defines which model/table to base the view on?
I did try get_context_data but believe that is not the solution, as I think it only pushes it to the rendered template.
You will see from my scripts - I am pushing the value 'syslog_policy' - i would like the view to set the model variable to be, what-ever value I push from the URL.
The reason for this, I have some pages/models that are similar - so rather than create multiple html pages and views - I wouldn't need to if I could get this to work.
URL Call
<li>Update/Delete Policies</li>
urls.py
path('HardenTemplateCreate/<str:element>', HardenTemplateCreate.as_view(success_url="/security_tooling/add_success") ,name="HardenTemplateCreate")
views.py
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['element']= self.kwargs['element']
print(context['element'])
return context
model = !!<NEED VALUE SUPPLIED IN URL HERE>!!
fields = ['name','template']
template_name = 'security_app/add.html'```

This would assume that all the models we are working with here belong to the same app (main) - if not, you also need to pass that to form kwargs and handle accordingly. In forms.py:
from django.apps import apps
from django.forms.models import fields_for_model
class VariableModelForm(forms.Form):
def __init__(self, *args, **kwargs):
model_name = kwargs.pop('model_name', None)
model = apps.get_model(app_label='main', model_name=model_name)
model_fields = fields_for_model(model)
super(VariableForm, self).__init__(*args, **kwargs)
for field in model_fields:
self.fields[field] = model_fields[field]
In your CreateView:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form']= VariableModelForm(model_name=self.kwargs['modelname'])
return context
You grab the model name from the url kwargs, pass it to your form as an extra positional argument, and use it in your form's init method to set up the fields.

Related

Subclass a view in django-registration-redux

I'm using Django-registration-redux and I want give more data to a view to render my base template. I read the example in doc.
My url.py:
class MyPasswordChangeView(PasswordChangeView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# context['book_list'] = Book.objects.all() # example in doc
context_dict = services.get_base_data_for_views(request)
return context_dict
urlpatterns = [
...
path('accounts/password/change/', MyPasswordChangeView.as_view(
success_url=reverse_lazy('auth_password_change_done')), name='auth_password_change'),
...
]
I have the extra data in services.py but this code gives error:
name 'request' is not defined
So context_dict isn't defined. Where can I take my request from? Mainly I need the user (but print(user)= 'user' is not defined). Or should I write another function?
In methods of Django class based views, you can access the request with self.request.
class MyPasswordChangeView(PasswordChangeView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context_dict = services.get_base_data_for_views(self.request)
return context_dict
Therefore you can access the user with self.request.user. Normally you would want to use login_required or LoginRequiredMixin so that only logged-in users can access the view, but in your case PasswordChangeView takes care of that for you.

request v self.request in Django

I was just helped by the answer found here: global name 'request' is not defined: overriding form_valid but that left me asking another question:
When do you use just request, and when do you have to use self.request - and why? The example I see in the docs https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#dynamic-filtering don't use either request or self.request in the examples. Now maybe that's because it is a class based view, but I don't think I'm alone in getting the syntax and usage of cbv v generic v functional mixed up.
UPDATE
My code
from django.shortcuts import render
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
from essell.models import Code
class CodeListView(ListView):
model = Code
template_name='statute.html'
def get_context_data(self, **kwargs):
context = super(CodeListView, self).get_context_data(**kwargs)
return context
from the docs
from django.views.generic.list import ListView
from django.utils import timezone
from articles.models import Article
class ArticleListView(ListView):
model = Article
def get_context_data(self, **kwargs):
context = super(ArticleListView, self).get_context_data(**kwargs)
context['now'] = timezone.now()
return context
MY CURRENT ERRORS
TypeError at /us/constitution
cannot convert dictionary update sequence element #0 to a sequence
class CodeListView(ListView):
model = Code
template_name='statute.html'
def get_context_data(self, **kwargs):
#context = super(CodeListView, self).get_context_data(**kwargs)
context = {'object_list':object_list}
return context
NameError at /us/constitution
global name 'object_list' is not defined
from the docs: https://docs.djangoproject.com/es/1.9/topics/class-based-views/generic-display/
"This template will be rendered against a context containing a variable called object_list that contains all the publisher objects. A very simple template might look like the following:"
class CodeListView(ListView):
model = Code
template_name='statute.html'
def get_context_data(self, **kwargs):
context = super(CodeListView, self).get_context_data(**kwargs)
context = {'object_list':object_list}
return context
global name 'object_list' is not defined
Like anything else in Python, any variable you reference must already exist in your current context.
Originally, Django views were simply functions that took as their first argument the request variable. Presumably, if you're writing old-style views, whatever you define as your first variable (e.g. def view_name(request, ...)) will be where the request is provided.
If you're using class-based views, then your views are member functions, so by convention their first argument will be self, e.g. def view_name(self, request, ...). In this case, refer to the Django documentation regarding which arguments are provided to which functions depending on which view you're subclassing.

#method_decorator with login_required and permission_required

I'm using Class-based views where I would like to ensure that each view can be access by logged-in users and by one type of the users only (there are two groups of users - each group has different permissions).
I'm implementing this according to the docs with persmissions (I'm using Django 1.7.7) https://docs.djangoproject.com/en/1.7/topics/class-based-views/intro/#decorating-the-class, however using two parameters raises an error " method_decorator() takes exactly 1 argument (2 given)" .
Hence - how to do it such that those two factors(login and permissions) will be verified in a class-based view?
class PatientCreate(CreateView):
model = Patient
fields = '__all__'
#method_decorator(login_required, permission_required('patient.session.can_add_patient'))
def dispatch(self, *args, **kwargs):
return super(PatientCreate, self).dispatch(*args, **kwargs)
Thanks!
In your case, permission_required will redirect to the login page for users that are not logged in, so you don't need to use login_required at all.
#method_decorator(permission_required('patient.session.can_add_patient')
def dispatch(self, *args, **kwargs):
...
If you really did need to use multiple decorators, then you can use a list in Django 1.9+
decorators = [other_decorator, permission_required('patient.session.can_add_patient')]
class PatientCreate(CreateView):
model = Patient
fields = '__all__'
#method_decorator(decorators)
def dispatch(self, *args, **kwargs):
...
You can also shorten the code by decorating the class itself:
#method_decorator(decorators, name="dispatch")
class PatientCreate(CreateView):
model = Patient
fields = '__all__'
On Django 1.8 and earlier, you can't pass a list to method_decorator or decorate the class, so you have to stack the decorators
class PatientCreate(CreateView):
model = Patient
fields = '__all__'
#method_decorator(other_decorator)
#method_decorator(permission_required('patient.session.can_add_patient'))
def dispatch(self, *args, **kwargs):
...
The decorators will process the request in the order they are passed to method_decorator. So for the examples above, other_decorator will run before permission_required.

django TemplateView and form

I have some problem to figure out how new django views (template view) and forms can works I also can't find good resources, official doc don't explain me how can get request ( I mean get and post) and forms in new django views class
Thanks
added for better explain
for example I have this form :
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
and this is the code for read and print the form (old fashion way):
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
well my question is how you can do the same thing with template view thanks
Use a FormView instead, i.e.
from django.views.generic import TemplateView, FormView
from forms import ContactUsEmailForm
class ContactView(FormView):
template_name = 'contact_us/contact_us.html'
form_class = ContactUsEmailForm
success_url = '.'
def get_context_data(self, **kwargs):
context = super(ContactView, self).get_context_data(**kwargs)
#context["testing_out"] = "this is a new context var"
return context
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
#form.send_email()
#print "form is valid"
return super(ContactView, self).form_valid(form)
More on FormView in Django Docs
Technically TemplateView can also be used, just overwrite the post method, since by default template view does not allow you to post to it:
class ContactUsView(TemplateView):
template_name = 'contact_us/contact_us.html'
def post(self, request, *args, **kwargs):
context = self.get_context_data()
if context["form"].is_valid():
print 'yes done'
#save your model
#redirect
return super(TemplateView, self).render_to_response(context)
def get_context_data(self, **kwargs):
context = super(ContactUsView, self).get_context_data(**kwargs)
form = ContactUsEmailForm(self.request.POST or None) # instance= None
context["form"] = form
#context["latest_article"] = latest_article
return context
I think the FormView makes more sense though.
I would recommend just plodding through the official tutorial and I think realization will dawn and enlightenment will come automatically.
Basically:
When you issue a request: '''http://mydomain/myblog/foo/bar'''
Django will:
resolve myblog/foo/bar to a function/method call through the patterns defined in urls.py
call that function with the request as parameter, e.g. myblog.views.foo_bar_index(request).
and just send whatever string that function returns to the browser. Usually that's your generated html code.
The view function usually does the following:
Fill the context dict for the view
Renders the template using that context
returns the resulting string
The template generic view allows you to skip writing that function, and just pass in the context dictionary.
Quoting the django docs:
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = "about.html"
All views.generic.*View classes have views.generic.View as their base. In the docs to that you find the information you require.
Basically:
# urls.py
urlpatterns = patterns('',
(r'^view/$', MyView.as_view(size=42)),
)
MyView.as_view will generate a callable that calls views.generic.View.dispatch()
which in turn will call MyView.get(), MyView.post(), MyView.update() etc.
which you can override.
To quote the docs:
class View
dispatch(request, *args, **kwargs)
The view part of the view -- the method that accepts a request
argument plus arguments, and returns a HTTP response. The default
implementation will inspect the HTTP method and attempt to delegate to
a method that matches the HTTP method; a GET will be delegated to
get(), a POST to post(), and so on.
The default implementation also sets request, args and kwargs as
instance variables, so any method on the view can know the full
details of the request that was made to invoke the view.
The big plusses of the class based views (in my opinion):
Inheritance makes them dry.
More declarative form of programming

How can I make a Generic Class Based Create View for a Model?

What I'm trying to do is Django boilerplate for functional views. Any help here is very much appreciated, as the docs show examples for the template view and list view, but I've found very little for the model-based generic views. Am I missing an example in the docs?
I have a model that represents an entry in a calendar. There's a foreign key to another object (not a user) that owns the entry. What I want to do is simply to create the entry, ensuring that the entry's foreign key is properly set and then return the user to the appropriate calendar page.
I don't know, though, how class-based generic views receive their URL arguments and I'm not clear on how to set the success_url so that it reuses the id that was originally passed to the creation URL. Again, thank you in advance for your help.
What I'm asking, essentially, is, what is the class-based generic view equivalent of the following:
def create_course_entry(request, class_id):
'''Creates a general calendar entry.'''
if request.method == 'POST':
form = CourseEntryForm(request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.course = Class.objects.get(pk=class_id)
new_entry.full_clean()
new_entry.save()
return HttpResponseRedirect('/class/%s/calendar/' % class_id)
else:
form = CourseEntryForm()
return render_to_response('classes/course_entry_create.html',
{ 'class_id': class_id, 'form': form, },
context_instance=RequestContext(request))
You could subclass the edit.CreateView generic view, set the class/course in the dispatch() method, and save this by overriding the form_valid() method:
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.views.generic.edit import CreateView
class CourseEntryCreateView(CreateView):
form_class = CourseEntryForm
model = CourseEntry
def dispatch(self, *args, **kwargs):
self.course = get_object_or_404(Class, pk=kwargs['class_id'])
return super(CourseEntryCreateView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.course = self.course
self.object.save()
return HttpResponseRedirect(self.get_success_url())
If you're not customising the CourseEntryForm ModelForm, then you can leave out the form_class property.
Unfortunately, it is not possible to call super() in the form_valid() method - due to the way it has been written would mean the object would be saved again.
If you need the Class (course?) instance in the template context, then you can add this in the get_context_data() method:
def get_context_data(self, *args, **kwargs):
context_data = super(CourseEntryCreateView, self).get_context_data(
*args, **kwargs)
context_data.update({'course': self.course})
return context_data
An alternative to Matt Austin's answer might be to override the get_form method:
from django.shortcuts import get_object_or_404
from django.views.generic import CreateView
class CourseEntryCreateView(CreateView):
form_class = CourseEntryForm
model = CourseEntry
def get_form(self, form_class):
form = super(CustomCreateView, self).get_form(form_class)
course = get_object_or_404(Class, pk=self.kwargs['class_id'])
form.instance.course = course
return form
This way, .course is on the CourseEntry instance in the context, and on the instance created when the form is saved upon POST.