Cannot get MEDIA_URL from Django widget's template - django

I am a new Djangoer, and figuring out how to build custom widget, my problem is cannot get the MEDIA_URL in my widget's template, while the form use MySelectWidget able to get the MEDIA_URL itself.
#
#plus_sign.html
#
<a href="" class="" id="id_{{ field }}">
<img src="{{ MEDIA_URL }}images/plus_sign.gif" width="10" height="10" alt="Add"/>
</a>
^ cannot load the {{ MEDIA_URL}} to this widget's template, and therefore I can't load the .gif image properly. :(
#
#custom_widgets.py
#
from django import forms
class MySelectMultiple(forms.SelectMultiple):
def render(self, name, *args, **kwargs):
html = super(MySelectMultiple, self).render(name, *args, **kwargs)
plus = render_to_string("plus_sign.html", {'field': name})
return html+plus
#
#forms.py
#
from django import forms
from myapp.custom_widgets.py import MySelectMultiple
class MyForm(forms.ModelForm):
contacts = forms.ModelMultipleChoiceField(Contact.objects, required=False, widget=MySelectMultiple)
#
#views.py
#
def AddContacts(request):
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
new = form.save()
return HttpResponseRedirect('/addedContact/')
else:
form = MyForm()
return render_to_response('shop/my_form.html', {'form': form}, context_instance=RequestContext(request))
#
#my_form.html
#
{% extends "base.html" %}
{% block content %}
{{ form.contacts }}
{% endblock %}
Please let me know how can I load the widget's image properly. Thank you so much for all responses.

Context processors only get applied when you use a RequestContext.
Your render method should be something like:
from django.template import RequestContext
def render(self, name, *args, **kwargs):
html = super(MySelectMultiple, self).render(name, *args, **kwargs)
context = RequestContext({'field': name})
plus = render_to_string("plus_sign.html", context)
return html + plus
And, as was mentioned by #czarchaic, make sure the media context processor is in TEMPLATE_CONTEXT_PROCESSORS (it should be by default).
Docs link.

Actually the correct way to do this is using Widget Media.
When defining your widget, you should define a Media inner class in which you should include a CSS file in order to style your widget. In this case make the <a> tag not to display text and have a plus sign background image.
class MyWidget(TexInput):
...
class Media:
css = {
'all': ('my_widget.css',)
}
If you really need to include the MEDIA_URL inside your rendered widget, I'd recommmend to import it directly from django.conf.settings and include settings.MEDIA_URL in your rendering context.
from django.conf import settings
class MyWidget(TextInput):
...
def render(self):
return render_to_string('my_widget.html', {
'MEDIA_URL': settings.MEDIA_URL,
...
})

Make sure the context processor is being loaded in settings.py
TEMPLATE_CONTEXT_PROCESSORS=(
...other processors,
"django.core.context_processors.media",
)
It is loaded by default if you don't specify TEMPLATE_CONTEXT_PROCESSORS, but if specified, the above processor must also be included.
http://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors

I think we can do in this way, to pass the RequestContext, in order to access the MEDIA_URL without making another variable, and passing other variables at the 2nd parameter of the render_to_string method.
If we use:
context = RequestContext({'field': name})
The {{ field }} in the widget's template is empty and not able to access.
Here is the block which can access the MEDIA_URL as well as the {{ field }}.
However, I agree using the inner Media class for complex javascript and CSS setting. However, for a simple image src path, I think this will do.
def render(self, name, *args, **kwargs):
html = super(SelectMultipleWithModalDialog, self).render(name, *args, **kwargs)
**context = RequestContext({})
popup_plus = render_to_string("widgets/modal_dialog_plus_sign.html", {'field': name}, context_instance=context)**
return html + popup_plus
Please correct me if this is not the good way of doing it. Thanks for all participants of this thread.

Related

Apply CSS to Django Form Wizard

I´m working with the Form Wizard which is alright but right now its not really pretty.
My Views.py is based on the docs:
class ContactWizard(SessionWizardView):
form_list = [DurationForm, ContactForm2]
def done(self, form_list, **kwargs):
return render(self.request, 'done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
My done.html contains:
{% extends "base.html" %}
{% block content %}
<h1> Done! </h1>
{% endblock %}
My forms are defined in forms.py:
from django import forms
class DurationForm(forms.Form):
Duration_in_secounds = forms.IntegerField()
class FrequencyForm(forms.Form):
Frequency_in_time_interval = forms.IntegerField()
Now i wonder how the forms even render because i never load them the way i used (for example with
{{form.as_p}}
Because whatever i change in my done.html doesnt affect the forms created with the wizard i have no idea how to add css to them.
Could any of you help me get whats going on here?
(sorry if the question is stupid/ already asked - its quite late here and i couldn´t find anything for the last two hours)
I think it doesn't matter it's a Form Wizard. You can just create a base class that you have DurationForm and ContactForm2 extend from. For example:
def __init__(self, *args, **kwargs):
super(BaseFormClass, self).__init__(*args, **kwargs)
# specific field
self.fields['name'].widget.attrs['class'] = 'my_class'
# all fields on form
for field in self.fields:
self.fields[field].widget.attrs['class'] = 'my_class'
Not tested.
You should add load static to your template (html):
{% load staticfiles %}
<link href="{% static 'myapp/css/my.css' %}" rel="stylesheet">
This is where the css, scripts, etc. are loaded.

What's the best way to render multiple views in the same template?

Scenario
I need to render 2 separate views from a third party app, in the same View. The views in question are for login and signup.
The template for each view then simply includes an inclusion tag to render a generic form.
Solution
The solution i've come up with is to register a tag for each view that creates a template.Node to render each one.
from django import template
from third_party_app import LoginView, SignupView
register = template.Library()
#register.tag
def login_form(parser, token):
return ViewNode(LoginView, template_name=get_template_name(token))
#register.tag
def signup_form(parser, token):
return ViewNode(SignupView, template_name=get_template_name(token))
class ViewNode(template.Node):
def __init__(self, view_class, **kwargs):
self.view_class = view_class
self.kwargs = kwargs
def render(self, context):
request = context['request']
self.kwargs['request'] = request
view = self.view_class(**self.kwargs)
response = view.get(request)
response.render()
return response.content
def get_template_name(token):
tag_name, template = token.split_contents()
return str(template[1:-1])
And the template for the main View looks like this:
<div>
{% login_form 'account/login.html' %}
</div>
... some other html ...
<div>
{% signup_form 'account/signup.html' %}
</div>
The template for each of the individual login and signup views only contains an inclusion tag to render another template for a generic form.
So accounts/login.html is simply this:
{% render_login_form %}
and the inclusion tag looks like this
#register.inclusion_tag('account/snippets/form.html', takes_context=True)
def render_login_form(context):
return {'form': context['form'],
'primary_btn_label': 'Sign In',
'secondary_btn_label': 'Forgot Password?',
'tertiary_btn_label': 'Sign Up?',
'col_offset': '3',
'col_width': '9'}
Question
It works, but i'm wondering 2 things.
Is this the best way to render 2 Views in the same View?
It feels like there are two many steps to achieve this. Is there a simpler way to solve this problem?
I have does something similar this way:
First two templates login.html and signup.html. In these templates I access context variables like form, primary_btn_label and other stuff you have in your render_login_form inclusion tag.
But then I have a custom context processor (See the Django documentation on context processors) that initializes these variables (like form and so on)
The context processor (save in myapp/context_processors.py) looks something like this:
from .forms import LoginForm, RegisterForm
def login_register_form(request):
login_popup_form = LoginFormForPopup()
register_popup_form = RegisterFormForPopup()
context = {
'login_popup_form': login_popup_form,
'register_popup_form': register_popup_form,
}
return context
The custom context processor is enabled in your Django settings file by adding it to the TEMPLATE_CONTEXT_PROCESSORS variable:
TEMPLATE_CONTEXT_PROCESSORS = (
...
'myapp.context_processors.login_register_form',
)
Hope this helps!

Rendering field errors in django-crispy-forms with inline forms

I'm using bootstrap3 as the default template pack in django_crispy_forms, and trying to render a form with the crispy tag:
{% crispy form %}
My form class has the following helper attributes:
class TheForm(forms.Form):
adv_var = forms.CharField(label="variable", max_length=70)
value = forms.FloatField()
def __init__(self, *args, **kwargs):
super(TheForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.form_class = 'form-inline'
self.helper.field_template = 'bootstrap3/layout/inline_field.html'
self.helper.layout = Layout(
'adv_var', 'value',
ButtonHolder(
Submit('submit', 'Start', css_class='button white')
)
)
When posting the form with errors, re-rendering the template does not show the errors even though I can print form._errors in the view and see the list of errors.
If I change the helper.field_template to another value (or remove it to set the default) the errors are displayed above each field - but I don't get the inline display anymore.
How can I use django-crispy-forms to display all errors of this form in a separate div for example?
We use django.contrib.messages to push a generic error string when the form has validation errors, and leave the field errors alone to render inline:
from django.contrib import messages
# ...
if not form.is_valid():
messages.error(request, "Please correct the errors below and resubmit.")
return render(request, template, context)
We then use bootstrap alerts to show all messages, including our generic error, though you could of course mark it up however you wanted.
But if all you want to do is move the errors into a separate block, add them to your request context:
from django.contrib import messages
# ...
if not form.is_valid():
context['form_errors'] = form.errors
return render(request, template, context)
and in your template:
{% crispy form %}
<div id='form-errors'>{{ form_errors }}</div>
You can then fiddle with the crispy form's helper attributes and styles to control the display of the inline errors.
Maybe the easier way is the next because it uses less imports...
.. in views:
if request.method == 'POST':
form = TheForm(request.POST)
if form.is_valid():
form.save()
return redirect('url_name')
else:
form = TheForm()
return render(request, 'form.html', {'form': form})
... and in the form you need only:
{% load crispy_forms_tags %}
{% crispy form %}
... where 'url_name' is defined name of pattern in urlpatterns (urls.py )... that's all you need really...
Crispy is a really smart system. The system knows how can intuitively to show the form errors.
You may want to consider using the FormView Generic View, especially if you are using crispy forms:
app/views.py:
from django.views.generic.edit import FormView
from django.urls import reverse_lazy
from .forms import MyForm
class MyFormView(FormView):
template_name = 'app/myform.html'
form_class = MyForm
success_url = reverse_lazy('success_url')
app/templates/app/myform.html:
{% load crispy_forms_tags %}
{% crispy form %}

What is the right way to pass global template variables in Django?

Suppose this is what we want in every page, we'd create "base_template.html"
<title>{% block title %}{{ page_title }}{% endblock %}</title>
{{ page_title }}{% endblock %}
Instead of passing page_title, domain, current_path from every view function such as:
def display_meta(request):
user_meta = request.META.items()
sorted_meta = sorted(user_meta) # a list of tuples
return render_to_response('meta.html', {'sorted_meta': sorted_meta,
'current_path': request.get_full_path(),
'domain': request.get_host(),
'page_title': display_meta.__name__})
# and repeat the dictionary same manner for other views....
#urls.py
('^book_list/$', 'object_get_list', {'model': models.Book}),
A different approach is wrapping view functions
# urls.py
('^book_list/$', views.get_template(views.object_get_list),{'model': models.Book}),
# views.py
def get_template(view, **extrakw):
def wrapview(request, **extrakw):
template_dict = {'current_path': request.get_full_path(), 'domain': request.get_host()}
extrakw['template_dict'] = template_dict
return view(request, **extrakw)
return wrapview
def object_get_list(request, **kwargs):
model = kwargs.pop('model', None)
model_name = model.__name__.lower()
template_dict = kwargs.pop('template_dict', None)
template_dict[model_name] = model.objects.all()
template_dict['page_title'] = model_name +" list"
template_name = '%s.html' %(model_name)
return render_to_response(template_name, template_dict)
Pro: Besides editing htmls, now modification is done in just one view, instead of every view.
Cons: Ugly URLConf and probably error propne too
Attempt 3:
Create a global dictionary just like template_dict I created.
template_dict = {/..../}
def view1()
def view2() ...
Problem: I can't use request.path (or anything has to do with request). This falls back to the previous attempt (wrapper).
But there must be an easier way. What is the proper way of passing global template variables throughout a django site so each view function is now indepenednt of gloabl templat variables?
Thank you for you time.
Use a context processor.
Add the name of your function to TEMPLATE_CONTEXT_PROCESSORS in settings.py.
A simple context processor I use is:
def common_request_parameters(request):
return {'home_login_form': AuthenticationForm(request)}

one template - multiple modelforms errors

views.py
def fadded(request):
if request.method == "POST":
fform = FtForm(request.POST)
bform = BgForm(request.POST)
if fform.is_valid() and bform.is_valid():
bcontent=bform.save()
fcontent=fform.save()
else:
return render_to_response("ft.html", {
"fform": fform,
"bform": bform,
},context_instance=RequestContext(request))
return HttpResponse('OK!')
ft.html
...
{% if form.errors%}
{% for error in form.errors %}
{{ error|escape }}
{% endfor %}
{% endif %}
...
There are two modelforms: fform and bform. They represent two different models, but are used in same template. I'm trying to save both and to get form-/fielderrors from both. But if there are already fform.errors, django doesn't shows bform.errors(and propably doesn't even create bform). Any proposals for a different way?
django doesn't shows bform.errors(and
propably doesn't even create bform)
Given your setup, both forms are passed data and are ready to be validated. There shouldn't be a problem.
In your template, you'd have to display both forms errors (I only see one form being checked in your template)
{{ fform.errors }} <!-- show errors from fform -->
{{ bform.errors }} <!-- show errors from bform -->
You need to use Class based views!
Here is a quick example of using multiple forms in one Django view.
from django.contrib import messages
from django.views.generic import TemplateView
from .forms import AddPostForm, AddCommentForm
from .models import Comment
class AddCommentView(TemplateView):
post_form_class = AddPostForm
comment_form_class = AddCommentForm
template_name = 'blog/post.html'
def post(self, request):
post_data = request.POST or None
post_form = self.post_form_class(post_data, prefix='post')
comment_form = self.comment_form_class(post_data, prefix='comment')
context = self.get_context_data(post_form=post_form,
comment_form=comment_form)
if post_form.is_valid():
self.form_save(post_form)
if comment_form.is_valid():
self.form_save(comment_form)
return self.render_to_response(context)
def form_save(self, form):
obj = form.save()
messages.success(self.request, "{} saved successfully".format(obj))
return obj
def get(self, request, *args, **kwargs):
return self.post(request, *args, **kwargs)
In this example I have found online (on RIP tutorial), we use TEMPLATE VIEW. Class-based views are your way around this. Here is a link to the most up-to-date documentation on how to use class-based views on Django. Have fun reading, and most of all, patience. https://docs.djangoproject.com/en/2.2/topics/class-based-views/
Hopefully, this could help to guide you in the right direction. Looking forward to hearing from how it goes.