Generate PDF with Django Class Based Views - django

I currently have a Library Loan template implemented using Class Based Views. The implementation is probably something that we have seen before:
In urls.py:
#PK is the member ID
url(r'^library/generate_loan_slip/(?P<pk>(\d+))/(?P<loan>(\d+))/'
,LoanSlipDetailView.as_view()
, name='library_app_generate_loan_slip'),
In views.py:
class LoanSlipDetailView(DetailView):
model = LibraryMember
loan = None
template_name = 'loan-slip.html'
def get_context_data(self, **kwargs):
context['loan'] = self.loan
context['member'] = member
return context
def get_object(self):
member = self.model.objects.get(pk=self.kwargs['pk'])
self.loan = LibraryLoan.objects.get(pk=self.kwargs['loan'], for_member=member)
return member
And in the HTML, loan-slip.html, the html with all the required variables in {{}} and controls using {% if %} ... {% else %} ... {% endif %} and {% for ... %} ... {% endfor %}.
Right now, I want the response to generate a PDF view. So far, I have checked out reportlab and they implemented it for function based views. However, is there an implementation for Class Based Views?

You can just override get method of DetailView and write reportlab logic over there.

Related

Pass other object/data into Flask Admin model view edit template

I'm extending the edit template for a ModelView so that I can show some other information from the database that is relevant for determining how to edit the record in this view. I know how to extend the template and get it to work, but I can't figure out how to query an object and use it in the template.
Also I need to use the value from the model/record in querying the new object I need to pass.
Here is my code from init.py:
class MilestoneView(ModelView):
edit_template = '/admin/milestone_model/milestone_edit.html'
can_delete = True
#i need something like this to work:
referrals = Referral.query.filter_by(email=model.email)
#then i need to pass referrals into the template
admin = Admin(app, name="My App", template_mode='bootstrap3')
admin.add_view(MilestoneView(Milestone, db.session, name='Milestones'))
Then from milestone_edit.html, I want something like this to work:
{% extends 'admin/model/edit.html' %}
{% block body %}
{{ super() }}
{% for r in referrals %}
<p>{{ r.name }}</p>
{% endif %}
{% endblock %}
But of course the referrals object is not available to use in the template. How do I customize this ModelView in order to pass this object in from the init file? I've reviewed the available posts on this subject(ish) on here and haven't found an answer. Thanks in advance.
Override your view's render method, see code on Github, and test if the view being rendered is the edit view. Now you can inject any data into the kwargs parameter. For example:
class MilestoneView(ModelView):
def render(self, template, **kwargs):
# we are only interested in the edit page
if template == 'admin/model/milestone_edit.html':
# Get the model, this is just the first few lines of edit_view method
return_url = get_redirect_target() or self.get_url('.index_view')
if not self.can_edit:
return redirect(return_url)
id = get_mdict_item_or_list(request.args, 'id')
if id is None:
return redirect(return_url)
model = self.get_one(id)
if model is None:
flash(gettext('Record does not exist.'), 'error')
return redirect(return_url)
referrals = Referral.query.filter_by(email=model.email)
kwargs['referrals'] = referrals
return super(MilestoneView, self).render(template, **kwargs)
Note how the model is retrieved. This is a direct copy of the code in method edit_view code. Adjust the code for your use-case.
Use the variable referrals in your edit Jinja2 template.
The render method is called in the following routes for each view:
'/' - i.e. the list view code
'/new/' - code
'/edit/' - code
'/details/' - code

Is there a way to pass model attribute into another attribute?

I'm using {% autoescape off %} to render html that I add through admin page. I want to get another variable of the model.
post.html
{% autoescape off %}
{{ post.content }}
{% endautoescape %}
Is it possible to pass another attribute of the same model into post.content? Something like that
post.content
<img src="{{ post.main_image.url }}">
Yup. Assuming there is a post, and it has a main_image, which has a url, there shouldn't be any problem. You may want to check in the template if you are not sure yourself first though. So to be safer, you should do:
{% if post and post.main_image %}
<img src="{{ post.main_image.url }}">
{% endif %}
Ok, I've finally made it. My goal was to create CMS-ish admin page of a model, where I could add raw html, django tags and variables directly to the content attribute along with other attributes like titles, categories, images and so on. I've managed to do it by using and customizing django-dbtemplates package.
pip install django-dbtemplates
First, fork template model from dbtemplates and add your model as a foreign key
blog/models.py
from dbtemplates.models import Template as CoreTemplate
class Template(CoreTemplate):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
Then just customize your admin.py to show template field as an attribute
blog/admin.py
from .models import Post, Template
class TemplateInline(admin.TabularInline):
model = Template
extra = 0
class PostAdmin(admin.ModelAdmin):
inlines = [TemplateInline, ]
Optional
You can generate html names of you template based on your model slug by modifying save function of the Template class
blog/models.py
from dbtemplates.models import Template as CoreTemplate
from dbtemplates.conf import settings
class Template(CoreTemplate):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
def save(self, *args, **kwargs):
self.last_changed = now()
slug = self.post.slug
self.name = f'{slug}-content.html'
if settings.DBTEMPLATES_AUTO_POPULATE_CONTENT and not self.content:
self.populate()
super(Template, self).save(*args, **kwargs)
Then in your html you can render this template with context manager
post.html
{% with post.slug|add:'-content.html' as content %}
{% include content %}
{% endwith %}
Now in your admin settings you can have just one big content field from basic Template class
blog/admin.py
from .models import Template
from django.forms import Textarea
class TemplateInline(admin.TabularInline):
model = Template
extra = 0
fields = ('content',)
formfield_overrides = {
models.TextField: {'widget': Textarea(attrs={'rows': 40, 'cols': 150})},
}
To remove default dbtemplates Template class from your admin panel just unregister it in your admin settings
blog/admin.py
from dbtemplates.admin import Template as CoreTemplate
admin.site.unregister(CoreTemplate)

How to Pull info from another model within a DetailsView template (without ForeignKey)?

I am trying to pull data from two models within a DetailsView template (in Django). There is of course a primary model (eg. Articles) associated with the view, which is easy to access. However, I want to access data from a model (eg. Terms). I do not want to use ForeignKey because I will be using many 'Terms' in each 'Article,' and since ForeignKey will allow me to link to only row in the Terms model, I will have to set-up mutiple ForeignKey fields, which can get messy fast.
I was thinking that this could be accomplished using get_context_data or templatetags, but haven't have had any luck yet. Any thoughts?
From Django Documentation you can add any queryset or context value you like to call in your template context like book_list below will list all books and it doesn't has to be linked with any other models..
#views.py
class PublisherDetail(DetailView):
model = Publisher
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(PublisherDetail, self).get_context_data(**kwargs)
# Add in a QuerySet of all the books
context['book_list'] = Book.objects.all()
return context
#yourtemplate.html
{% for book in book_list %}
{% if book %}
{{ book.title }}
{% endif %}
{% empty %}
No book_list found.
{% endfor %}

How to pass two or more objects from views for render it in template

Im working on Django, i need pass two or more objects to a view, for render it in the template. I mean,
i have one object, and this object can has two or more objects from other model, in the view i have:
def infoUsuario(request,id_usuario):
user = info_productor_cultivador.objects.get(id=id_usuario)
familiar = grupo_familiar.objects.filter(familiar_de_id=user)
ctx = {'usuario':user,'familiar':familiar}
return render_to_response('usuarios.html',ctx,context_instance=RequestContext(request))
in the template:
{% for familiares in familiar %}
<p>{{familiar.primer_nombre}}</p>
{% endfor %}
The models:
class grupo_familiar(models.Model):
familiar_de = models.ForeignKey(info_productor_cultivador)
primer_nombre = models.CharField(max_length=50)
class info_productor_cultivador(models.Model):
primer_nombre = models.CharField(max_length=50)
First, instead "filter" in familiar object i has "get" but said me: "get() returned more than one grupo_familiar -- it returned 2!" looking for a solution i found it that i have to pass the "filter" query, this time i dont have errors from Django, but the "familiar" object does not render it in the template.
In other words, i think that i need is how to pass a foreign key in the view and render it in the template.
Thanks
views
from django.shortcuts import render
def info_usuario(request, id_usuario):
user = info_productor_cultivador.objects.get(id=id_usuario)
familiar = grupo_familiar.objects.filter(familiar_de_id=user)
ctx = { 'usuario': user, 'familiar': familiar }
return render(request, 'usuarios.html', ctx }
template
{% for familiares in familiar %}
<p>{{ familiar.primer_nombre }}</p>
{% endfor %}

django - 2 views in one template

i have 2 different views which are getting filtered data from db. and i have to use these views in one template file(admin.html) but i cant use multiple views on a page at same time.
here is my view1:
draft_list = Post.objects.filter(isdraft=True).order_by("-posted")
return render_to_response('userside/admin.html',
{'draft_list':draft_list,},
context_instance = RequestContext(request))
view2 :
publish_list = Post.objects.filter(isdraft=False).order_by("-posted")
return render_to_response('userside/admin.html',
{'publish_list':publish_list,},
context_instance = RequestContext(request))
i d like to use them like :
{% for d in draft_list %}
{{ d.title }}
{% endfor %}
--
{% for p in publish_list %}
{{ p.title }}
{% endfor %}
i want to make these 2 views 'one view' .what is the correct way?
You do not want to have 2 views in 1 template (which is not possible anyway), but have 2 models available in 1 template for rendering. Just do it like this:
draft_list = Post.objects.filter(isdraft=True).order_by("-posted")
publish_list = Post.objects.filter(isdraft=False).order_by("-posted")
return render_to_response('userside/admin.html',
{'draft_list':draft_list,'publish_list':publish_list})
From your question, it seems that you're using function based views. An alternative way to solve the problem you're having is to use class based views and override the get_context_data method to pass your template two contexts, one for each queryset.
#views.py
class MyClassBasedView(DetailView):
context_object_name = 'draft_list'
template='my-template'
queryset = Post.objects.filter(isdraft=True).order_by("-posted")
def get_context_data(self, **kwargs):
context = super(MyClassBasedView, self).get_context_data(**kwargs)
context['publish_list'] = Post.objects.filter(isdraft=False).order_by("-posted")
return context