I have a view that returns an HTML response using render. The data returned is specific to a template.
If I want to consume that data on another page, should I write another view with the same logic, or is there a way to pass the data from view 1 to view 2
view
return render(request, 'pages/project_details.html', {"project": project,
urls
path('show_project_details/<project_id>', view=show_project_details, name="show_project_details"),
I know that a view is just a function, which takes a request and does something with it, but I don't get how I make a request to a view within a template and consume the response within an existing page.
Example here:
## filename: views.py
from django.shortcuts import render
from .models import Team
def index(request):
list_teams = Team.objects.filter(team_level__exact="U09")
context = {'youngest_teams': list_teams}
return render(request, '/best/index.html', context)
I want to return the data within this context to home.html and index.html.
Hope this makes sense?
You can use your urls.py to pass a variables through via the URL notation.
For example - Getting Team info:
template
{% for team in youngest_teams %}
Team
{% endfor %}
url.py
path('team_info/<team_id>', view=team_info, name="get_team_info")
views.py
def team_info(request, team_id=None):
team = Team.objects.get(id=team_id) #Information on team is passed.
There are multiple cases in your case. Let me explain them one by one.
Let's say you have two templates. One is template 1 and the other is template 2.
If you want to use the same logic for template 1 and template 2. You can use a variable in the views.py & url.py and use a condition there on that variable which is team_id in your case. E.g.
def index(request, team_id):
list_teams = Team.objects.filter(team_level__exact="U09")
context = {'youngest_teams': list_teams}
if team_id == 1:
return render(request, '/best/index.html', context)
elif: team_id == 2:
return render(request, '/best/template2.html', context)
And in urls.py, you can write:
path('team_info/<int:team_id>', view=team_info, name="get_team_info")
If you don't want the variable in the views and URLs. Then you can write different views with the same code in views.py. But in the case of multiple ids, it will cause code duplication.
Related
I want to pass some html as a string in a variable to a template, which also includes a link: <a href= {%url: 'app:page'%}> If I pass the variable though, the template tags are not invoked. If I use {{variable | safe}} to escape the restrictions, still it is not being called. I think I have a major error in my thinking, or is it just not possible to do this? The idea is to ultimately load those strings from the database to show users customized webpage elements.
view.py:
def custom(request, slug):
link = """<li>Link</li>"""
return render(request, 'basics/custom.html', {'link': link})
and in the webpage custom.html:
{{link | safe }}
You should use format_html to build a HTML fragment for use in your templates, and reverse for resolving a url
from django.urls import reverse
from django.utils.html import format_html
def custom(request, slug):
link = format_html(
'<li>Link</li>',
reverse('app:page'),
)
return render(request, 'basics/custom.html', {'link': link})
I'm trying to send an object obtained with a ModelChoiceField into a view via urls and views parameters by I don't think that's the right method. I don't know if I have to use POST or GET method.
When I use the GET method, the object is displayed in the url but the view stay the same.
With the POST method, django send errors messages about parameters.
EDIT : I don't remeber the exact messages, I can't recover them for now but as I remember they said something like Reverse for argument not found
My form
class IndividuForm(forms.Form):
individu = forms.ModelChoiceField(queryset=Individu.objects.all()
Here's the view with the form
def index(request):
individu_form = IndividuForm(request.GET or None)
if individu_form.is_valid():
individu_id = individu_form.cleaned_data['individu'].id
HttpResponseRedirect('%i/raison/' % individu_id)
return render(request, 'derog_bv/index.html', {'individu_form':individu_form})
The template where the form is displayed
<form action="{% url 'index' <!-- Errors when I put parameters here --> %} method="<!-- GET or POST -->">
{% csrf_token %}
{{ form }}
<input type="submit">Suivant</input>
</form>
The view where I want to get my object
def raison(request, """ error with this parameter : individu_id"""):
individu = get_object_or_404(Individu, pk=individu_id)
URLs
urlpatterns = [
path('', views.index, name='index'),
path('<int:individu_id>/raison/', views.raison, name='raison'),
]
Ok, so:
1/ you definitly want to use the GET method for your form (you're not submitting data for processing / server state change)
2/ I don't know why you'd want to pass parameters (nor which parameters FWIW) to the {% url %} tag in your index template - you're submitting the form to the index view, which doesn't expect any additional param. Actually you could just remove the action attribute of the HTML form tag since the default (submitting to the current url) is what you want.
3/ your views.raison prototype must match the url definition, so it has to be:
def raison(request, individu_id):
# code here
4/ in your index view:
HttpResponseRedirect('%i/raison/' % individu_id)
you want to build the url using django.core.urlresolvers.reverse instead :
from django.core.urlresolvers import reverse
def index(request):
# code here
if ...:
next = reverse("raison", kwargs={"individu_id": individu_id})
return HttpResponseRedirect(next)
or - even easier - just use the redirect shortcut:
from django.shortcuts import redirect
def index(request):
# code here
if ...:
return redirect("raison", individu_id=individu_id)
There might be other issues with your code but from the infos you posted, those hints should at least put you back on tracks.
I have a class-based view FooCreate and I want to use it on two different pages:
normal create view: With all the normal header and footer parts: Extending my base.html
in popup: Here I want no visible header and footer part: Extending (the not existing yet) base_popup.html
I would like to implement this without a single "if", since I like condition-less code :-)
I would advise you to checkout how Django Admin handles this with IS_POPUP_VAR.
Basically, Django Admin uses a '_popup' parameter passed in the querystring. You can then pass a "base_layout" variable to context.
class MyView(View):
def get_context_data(self, **kwargs):
if '_popup' in request.GET:
kwargs['base_layout'] = 'base_popup.html'
return super().get_context_data(**kwargs)
And your templates would start with:
{% extends base_layout|default:"base.html" %}
I am using inclusion_tags to generate portions of my pages that repeat in many different places across my site.
templatetags/tags.py
#register.inclusion_tag('chunk_template.html')
def output_chunk(object, edit):
... # Lots of set up work here that I don't want to duplicate in the view
return { ... }
Upon AJAX submit of a form on the page, I need to refresh the very same HTML outputted by output_chunk(). To avoid completely rewriting output_chunk() in a view, I did the following as recommended in this answer about how to use templatetags in views:
views.py
def chunk(request, ...):
context = Context({..., 'request': request })
template_string = """
{% load output_chunk from tags %}
{% output_chunk ... %}
"""
t = Template(template_string)
return HttpResponse(t.render(context))
This is all working fine, except chunk_template.html calls {% csrf %}, which works when I call the template tag the standard way, but not when I call in this somewhat hacky way (to avoid writing the same code twice).
(For simpler template tags, this works fine when I call return render (request, template_name, context) from within the view.)
So, is there a better way to call the template tag from within the view to get all the middleware invoked properly? Or is there something I can add to this hack to make it work properly?
I don't understand the core of the problem, but you can always manually pull the token (the middleware calls this function).
from django.middleware.csrf import get_token
csrf = get_token(request)
Need to make the context a RequestContext.
context = RequestContext(request, {....})
{% include 'django.contrib.auth.views.login' %}
I don't want to write everything by hand.. I hate this really, django full of automatic stuff.
Goal is to include registration/login.html into base.html, so that I could have this form in every page
If I include only template itself (registration/login.html), problem appears that "form.login", I mean "form" var is not defined because this one comes from VIEW which called when you going to login url. So how can I call that view MANUALLY with include or at least to grab django.contrib.auth.views.login variables by my self in my own view and pass then to base.html?
P.s. It's not just about login form, I think there will be more situations like this
I have found better solution in #django irc.
They called inclusion tags
I'll give you my code, because I got lot's of problem learning new stuff in django =)
file: templatetags/form_login.py
from django import template
register = template.Library()
from django.contrib.auth.forms import AuthenticationForm
#register.inclusion_tag('registration/login.html')
def form_login():
return { 'form': AuthenticationForm() }
Now you can have your form anywhere, this will prerender template and THAT'S IT! no stupid context processors which requires to modify whole project settings.py, which is really sux if you writing stand alone little application..
If you need login-form on every page
Create a context processor:
def login_form_processor(request):
return {
'login_form': LoginForm(request.POST or None)
}
Add it to settings.CONTEXT_PROCESSORS.
Include the template for login form:
{% with login_form as form %}
{% include "registration/login.html" %}
{% endwith %}
You can also make you form lazy-loading, so form will not be created until it is used for the first time.
from django.utils improt functional
def login_form_processor(request):
create_login_form = lambda: LoginForm(request.POST or None)
return {
'login_form': functional.lazy(create_login_form, LoginForm)
}
But I guess you won't want the lazy-loading feature, because login-form is cheap to initialize.
Reusing views
Concerning the "grabbing variables" part from your question: you cannot grab variable from view. Django view is method which returns response object. You can not get variables from response. However some of views accept extra_context and other attributes. Those attributes allow you to configure those views in urls, or to wrap them with your own view, for example:
def my_login_view(request):
some_extra_data = get_some_data()
extra_context = {
'some_extra_var': some_extra_data
}
return login_view(request, extra_context=extra_context, template="my_template.html")
This is not exactly grabbing the variables from views, more like augmentation of existing views.
If you expect to have more situations like this, do less data-porcessing in views. Call some methods which checks for permissions. Collect some data from context-processors. Return rendered response. Now you can reuse the data in other views.
You can specify the action on the form html to point to the URL that accesses the corresponding view.
If you want a form, say called as login_form always populated in all templates, then put it in the context_processors.
Browsing the code for django.contrib.auth.views, you will see that the variables form, site and *site_name* are passed to the template.
Either you (1) provide your custom registration form or (2) you can just import django.contrib.auth.forms.AuthenticationForm in your view if you want to use it.