How to put multiple models on the same page? - django

I'm doing a website in html and base (where all pages extend) I want
to put a session of social network icons. As this session is base on
html it should be displayed on all pages of the website.
I do not want
to put this session in a static html, I want to do in django using
models. This is already done.
Question: Do I have to put the session of social network icons on each view, or can I make a separate view and all others extend this view?
How can I do this?

Try using an inclusion tag. You can create a function for doing all of the work to create the sessions and then associate that with a particular block of HTML.
templatetags/session.py
#register.inclusion_tag('includes/session_box.html')
def output_session_box(...):
...
return { .. }
The associated template file, includes/session_box.html, would have the HTML like any template.
And then your base.html would have:
{% load session %}
{% output_session_box ... %}

Use RequestContext and a context_processor to inject template variables into every view using RequestContext.
It's as simple as a python function accepting request as an arg, and returning a dictionary to be passed into your template.
https://docs.djangoproject.com/en/dev/ref/templates/api/#django.template.RequestContext
def my_processor(request):
return {'foo': 'bar'}
TEMPLATE_CONTEXT_PROCESSORS = (
# add path to your context processor here.
)
I generally have a per-project processor for the basics... It's exactly how django adds {{ user }} or {{ STATIC_URL }} to every template.

Related

How to access server data in a javascript file that is loaded by Django admin pages

I need to access server data in a javascript file that gets loaded with an admin page. In my case, I'll need a json variable that is set in the settings.py file (in the production version, this json-variable gets loaded from env-variable which means that the content of the variable is known at startup-time but is not hard coded).
I use the javascript to validate and populate some fields in an admin-page - that is a better UX than doing it server side.
Is it possible to add script-tag to the admin html-pages' head section, that contains server data, i.e. non-static data?
If I understood your question correctly, you need to send some data to the admin from the website, and yes there are multiple ways to do so
make an API that serves the data and fetch it using ajax from the admin
add the data in the context of the page and use it directly in the admin
in both ways you'll need to edit the admin and add some js code to it so that's what I'll show you here
Editing the admin
First Approch
here we gonna create a js file in the static files and then import it using the js variable in the Media class in the admin
class TestAdmin(admin.ModelAdmin):
class Media:
js = ('js/some_js_file.js',)
and then write an API to serve your data and consume it from the js
Second Approach
we will extend the add form template and write the js code directly in the template
{% extends 'admin/change_form.html' %}
{% block extrahead %}{{ block.super }}
# write code here
{% endblock %}
and in the admin
class TestAdmin(admin.ModelAdmin):
add_form_template = 'path_to_template'
note: you will need to enable the templates in the settings
now if you don't like the API approach, you could overwrite the admin add_form function and add the context you need
class TestAdmin(admin.ModelAdmin):
add_form_template = 'admin/test_form_Change.html'
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['some_data'] = 'value'
return super(TestAdmin, self).changeform_view(request, object_id, extra_context=extra_context)
and you could use this tag
to add the data to the template
The answer from Mina Atef helped me to get this working. Thanks Mina Atef.
Here is the exact code that I used, in case if it helps someone. This works in Django 4.1. I needed this in list view but you can also use this approach in detail view.
Adding to the admin class:
class PlaceNameAdmin(admin.ModelAdmin):
# A lof of code not shown
class Media:
# Adding the javascript file
js = ('multi_line_list_edit.js',)
def changelist_view(self, request, extra_context=None):
extra_context = extra_context or {}
# Adding the extra context. It comes from settings but it can be any dynamic data.
# The REGISTER_PLACES_SETTINGS is actually not static, since it comes
# from env-variable that is set in production docker environment.
extra_context['register_places_settings'] = settings.REGISTER_PLACES_SETTINGS
return super(PlaceNameAdmin, self).changelist_view(request, extra_context)
A file named change_list.html needs to be added to the templates/admin/ directory. See the django doc for more info on how to override admin templates.
{% extends "admin/change_list.html" %}
{% block extrahead %}
{{ block.super }}
{% comment %}
See docs.djangoproject.com/en/4.1/ref/templates/builtins/#json-script for doc on how to use json_script
{% endcomment %}
{{ register_places_settings|json_script:"register_places_settings" }}
{% endblock %}
Here is how the data get used in multi_line_list_edit.js. This javascript file needs to be placed in the static/ directory.
$(document).ready(function() {
const register_places_settings = JSON.parse(document.getElementById('register_places_settings').textContent);
// A lof of code not shown
});

Retrieving ID from URL

Django question here. So in my base.html, I have a header. The header has links that go to user specific pages. That is, URL's like: /1234/home, /1234/about, etc. where 1234 is the id of a user. My question is, if I want to create a url pointing to a django view method called home with signature home (request, user=0) where user is a different user from the logged in user, how would I do this in the template? To clarify, I am using Django's {% url ... %} template tag and I can't just do {% url [view_name] request.user.id %} because I want the user id from the url, not the id of the logged in user. Additionally, I would prefer avoiding passing a template variable to get the id of the user in the url because then I would have to do that on every page.
Get the id you want as a kwarg to your view, and pass it as a variable to your template through the context to use in the {url ...}.
If you want to persist a variable across all your templates, look at context processors. They are added to each RequestContext so they are accessible to all templates assuming you use the render shortcut or pass the RequestContext to your template.render.
https://docs.djangoproject.com/en/dev/ref/templates/api/#subclassing-context-requestcontext
https://github.com/django/django/blob/stable/1.5.x/django/core/context_processors.py
There are some default ones that django uses. It's fairly simple to add your own.
I imagine your context processor will look something like
context_processors.py
def id_from_url(request):
# Find url id component in request.path
return { 'url_id' : url_id }
Then just add to your settings file appname.context_processors.id_from_url

Loading views into template without Ajax on Django

Good evening people. I have this Django question.. I have a view that renders a form with a header information from a master-detail like models. Below this form, I want to load another view that renders a form that fills the detail table and next to it, the list with the detail. I want to load inside the template of the header view, the detail's view but I want to do it without ajax. I was reading the {% include %} tag but, from what I understand, it loads a template, not a view. I might be wrong, is there a way to load a view inside the master's template?
Thanks in advance
You couldn't render a view like that without ajax. {% load %} is used for loading modules, not templates or views, like:
{% load humanize %}
However, you could use an {% include %} tag, or a custom template tag that does whatever processing you need and returns its template where you include it.

Including non-form in a Django SessionWizardView

Can a non-form web page be included in a django SessionWizardView?
For example, I want the user to FillOut Form1, Form2, Then View a web page (in same session) (click next), and then Form3? All this while maintaining the same session.
If so, how is this best accomplished? Any examples or snippets?
There's a fairly easy hack for this. Create a plain old form that has one field that is hidden from the user, has no content, and isn't required.
I do this:
class BlankForm(forms.Form):
nothing = forms.CharField(required=False, widget=HiddenInput)
Include it in your SessionWizardView call just like the other pages:
SessionWizardView.as_view([Form1, Form2, BlankForm, Form3])
In the template page you can use some logic like this to display info:
{% if wizard.steps.current == '2' %}
Whatever you want to show on the BlankForm
{% endif %}

Django: How to include modelform?

{% 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.