I'm migrating a project from Django 1.2 to Django 1.5. The project used function-based views such as this:
def notecard_product(request, stockcode):
if request.user.is_authenticated():
liked = Recommendation.objects.values_list('product_id',flat=True).filter(recommended=True, user=request.user)
unliked = Recommendation.objects.values_list('product_id',flat=True).filter(recommended=False, user=request.user)
extra_context = {"liked" : liked, "unliked":unliked}
else:
extra_context = {"liked" : [0], "unliked": [0]}
return object_detail(request, queryset=Product.objects.live(),
object_id=stockcode,
extra_context=extra_context,
template_name='products/notecard.html', template_object_name='notecard_product')`enter code here`
In this excerpt, stockcode is captured from the URL and used to determine object_id. So I'm wondering how I would do this in a class-based view. This is what I have so far:
class NotecardProductListView(ListView):
queryset=Product.objects.live()
pk=self.kwargs['stockcode']
template_name='products/notecard.html'
context_object_name='notecard_product'
def get_context_data(self, **kwargs):
context = super(BooksListView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated():
liked = Recommendation.objects.values_list('product_id',flat=True).filter(recommended=True, user=self.request.user)
unliked = Recommendation.objects.values_list('product_id',flat=True).filter(recommended=False, user=self.request.user)
extra_context = {"liked" : liked, "unliked":unliked}
else:
extra_context = {"liked" : [0], "unliked": [0]}
context.update(extra_context)
return context
pk is the new name for the old object_id kwarg. Obviously, this code doesn't work, because I can't access self outside of a function. But I'm not really sure how to do this. I need to set pk to something in the keyword arguments, but can't find a way to do this, as pk needs to be set in the class body outside of any functions. I also don't really have a way to experiment and try things, because the entire project is broken right now due to deprecated function calls.
Thanks!
I'm not sure where you got the idea that pk is the new name for object_id, nor why you think that you need to set it to a value per request. The point of the class-level attributes in class-based views is that they are set per view class, not per instance: they refer to the place the view will go to look up the value, not the actual value itself.
Your first mistake is that the equivalent of the old object-detail view is, not surprisingly, DetailView, not ListView. As the documentation shows, ListView gets the ability to show an object detail via its inheritance from the SingleObjectMixin. That mixin expects a class-level attribute called pk_url_kwarg, which is the name of the argument captured from the URL which identifies the object's PK: in your case, this is the string 'stockcode'. The instance itself takes care of looking up that value in any particular request, you don't need to do it.
Related
I am back with more django questions on CBVs. This is about context_object_name. I have the following:
#method_decorator(verified_email_required, name='dispatch')
class Create(CreateView):
model = Profile
context_object_name = 'profileForm'
template_name = 'Members/template_includes/profile/form.html'
form_class = ProfileForm
success_url = '/Members'
form_title = "New Login Profile Information"
def get(self, request, *args, **kwargs):
return render(request, self.template_name, {
'profileTitle': self.form_title,
})
I am using PyCharm and can put a breakpoint in the template_name form and see what the environment knows about. I expect to see a dict named profileForm with all the form members in it plus profileTitle. Instead I see profileTitle as a standalone member. I do not see anything named profileForm or object_list and the expected form members are not being painted in the template.
I suppose that I understand that the extra content in the return render will pass a "naked" profileTitle but I did expect that the default get behaviour would pull in the form info.
Have I missed the point?
You've overridden the get method in your CreateView-subclass and in doing so, you've bypassed the included functionality that a CreateView does to fill your context. If you take a look here you can see that a CreateView would otherwise call return self.render_to_response(self.get_context_data()) (because it inherits from ProcessFormView) and it's within get_context_data() (ref) that those included context variables are set up.
I've been learning Django and one source of confusion I have is with class based views and when to override the get method. I've looked through the documentation and it explains what get does but it doesn't explain when I should override get.
I originally created a view this way:
class ExampleView(generic.ListView):
template_name = 'ppm/ppm.html'
paginate_by = 5
def get(self, request):
profiles_set = EmployeeProfile.objects.all()
context = {
'profiles_set': profiles_set,
'title': 'Employee Profiles'
}
return render(request, self.template_name, context)
But I was recently told that my code was simple of enough for the default implementation, and that all I needed was this:
class ExampleView(generic.ListView):
model = EmployeeProfile
template_name = 'ppm/ppm.html'
So my Question is this: In what scenario/circumstance should I override the get method?
If you are using the builtin generic views, then you should rarely have to override get(). You'll end up either duplicating lots of functionality, or break features of the view.
For example, the paginate_by option will no longer work in your view, because you are not slicing the queryset in your get() method.
If you are using a generic class based view like ListView, you should try to override specific attributes or methods where possible, rather than overriding get().
The advantage of your view which overrides get() is that it's very clear what it does. You can see that the view fetches a queryset, includes it in a context, then renders the templates. You don't need to know about ListView to understand the view.
If you like the explicitness of overriding get() subclass View instead. You aren't using any of the features of ListView, so it doesn't make sense to subclass it.
from django.views.generic import View
class ExampleView(View):
template_name = 'ppm/ppm.html'
def get(self, request):
...
You should override the get method when you specifically want to do something other than the default view does. In this case, your code isn't doing anything other than rendering the template with the list of all EmployeeProfile objects, which is exactly what the generic ListView would do.
You might override it if you want to do something more complicated. For example, maybe you want to filter based on a URL parameter:
class ExampleView(generic.ListView):
template_name = 'ppm/ppm.html'
def get(self, request):
manager = request.GET.get('manager', None)
if manager:
profiles_set = EmployeeProfile.objects.filter(manager=manager)
else:
profiles_set = EmployeeProfile.objects.all()
context = {
'profiles_set': profiles_set,
'title': 'Employee Profiles'
}
return render(request, self.template_name, context)
I have a created a view class "class Demo", which has 3 functions
def update_time()
def get_context()
def before_response()
urls.py : url(r'^demo/$', Demo.as_view(),name='demo_class'),
When i'll enter url /demo/ how will it determine which function to call from "class Demo" ?
Because Django’s URL resolver expects to send the request and associated arguments to a callable function, not a class, class-based views have an as_view() class method which serves as the callable entry point to your class. The as_view entry point creates an instance of your class and calls its dispatch() method. dispatch looks at the request to determine whether it is a GET, POST, etc, and relays the request to a matching method if one is defined, or raises HttpResponseNotAllowed if not.
just read the docs
Basically class based views are recommended when you need to handle both get and post requests at one point. For example in get method of class Register, you can render the registration form and in its post method, you can handle the form submission. If its a get request it will automatically invoke the get() method in the class, same for post request. Also you can write any common code in the dispatch() method which will be invoked for every request.eg:
class Register(View):
def dispatch(self, *args, **kwargs):
'''
common code here
'''
return super(Register, self).dispatch(*args, **kwargs)
def get(self, request):
registration_form = RegistrationForm()
return render(request, 'new.html', { 'form': registration_form })
def post(self, request):
registration_form = RegistrationForm(request.POST or None)
if registration_form.is_valid():
#save form
return HttpResponseRedirect(reverse('success-show'))
return render(request,new.html', { 'form': registration_form })
For references you can check this website.
You need to subclass a class based views, and depending on that it will have one or another method.
For example TemplateView renders a template you pass in the template_name attribute.
All class based views care about is that the attributes needed to work properly are setted. That is done via different methods. You can check the django's documentation for specifics.
For example, if you want to render a form in your template view, you will need to pass the form in the context, so you can override get_context_data() like:
def get_context_data(self, **kwargs):
context = super(DemoClass, self).get_context_data(**kwargs)
context['form'] = MyForm()
return context
There are different methods to handle different things, like querysets, objects, etc.
They are not complicated, but they are specific. If a class based view does not fit what you need, it may be easier to implement the functionality from a more general view (View, TemplateView) than forcing a more specific one to do things it is not intended for.
slightly change the url
add numbers one to three in url and put the condition in your view.
Ex.
url(r'^abc/(?P<newid>.*)$', 'class_demo'),
so your url will be like abc/1 or abc/2 or abc/3
view
def class_demo(requests, newid):
if newid==1:
update_time()
elif newid==2:
get_context()
This may seem very basic, but, though I can guess, how do you make a view within a view, or more accurately, a dynamic template within a dynamic template. You see, I have these blocks of html which contain stats on certain things. How would I create a view that returned one of these boxes so another view could insert them into a template block? Is it "ok" to just have a function which returns it, or do I "have to" follow the same functionName(request) thing instead of functionName(info1, info2, info3) like a normal function. This seems like such a common thing that there would be some sort of standard.
This is exactly what custom template tags - in particular inclusion tags - are for.
If I understand you, you're basically talking about pre-populating the template context. Basically, you want a common bit of context to be present in a number of views, but you don't want to repeat yourself for each view. Using function-based views, the best way is to simply have generic method that takes care of this:
def add_some_context(context={}):
context['foo'] = 'bar'
return context
def view1(self, request):
context = {
'something1': 'blah',
}
return render_to_response('template1.html', add_some_context(context), context_instance=RequestContext(request))
def view2(self, request):
context = {
'something2': 'blah',
}
return render_to_response('template2.html', add_some_context(context), context_instance=RequestContext(request))
Both views will have a foo context variable you can use, then. Doing it with class-based views, you can create a mixin class:
class MyViewMixin(object):
def get_context_data(self, **kwargs):
context = super(MyViewMixin, self).get_context_data(**kwargs)
context['foo'] = 'bar'
return context
class MyView1(MyViewMixin, DetailView):
...
class MyView2(MyViewMixin, ListView):
...
When it comes time to add this to the actual template, you can have each view's template inherit from a template that implements the foo context variable in some way.
If it's something that should be applied to every view, then a context processor is more appropriate, but if it's just for a handful of views, then these two methods will serve you well.
Is it possible to manually add field entries to an object that has been created with the generic view?
Eg,
from django.views.generic.create_update import create_object
def create_thing(request):
queryset = Things.objects.all()
#Somehow pass something to tell the view to
#assign Thing.author=request.user...
return create_object(
request,
queryset,
form_class = ThingForm,
template_name = 'thing_template.html',
)
Ta!
Sorry, no. You'll have to write your own view.
Here's the source - you can see that there's no callback or anything, and nothing that gets passed both the request (so you can access request.user) and the new_object that you could possibly override to do the assignment.