Making a function available to all models - python

I have the following function in one of my models
def get_fields(self):
return[(field.name, field.value_to_string(self)) for field in MusicPack._meta.fields]
Which helps me iterate over all the fields of a model and display them into a template. How would I implement this to all my models without replicating the two lines in every model in my database?
Would I just make a superclass that contain the function for all models and then have all my models children of it?
template.html
<div id = "subtemplate">
<ul>
{% for model in object_list %}
<div class = modeldiv>
{% for name,value in model.get_fields %}
<li>
{% if value %}
{{ name|capfirst }} : {{ value }}
{% endif %}
</li>
{% endfor %}
</div>
{% empty %}
<li> No objects created yet. </li>
{% endfor %}
</ul>
</div>

You could use Mixins.
Example
class Mixin(object):
def get_fields(self):
return[(field.name, field.value_to_string(self)) for field in self.__class__._meta.fields]
class A(models.Model, Mixin):
...
class B(models.Model, Mixin):
...

Putting that method in a base class is certainly one way to do it. Another approach is to have it be a generic utility function in some utility module that prints all fields in an object, which you can call before rendering your template.
from myproject.utilities import get_fields
...
return render_template("template.html", get_fields(model))
Yet a third approach is to write a class decorator which provides this functionality to a class:
class with_get_fields(cls):
def __init__(self, *args, **kwargs):
cls.__init__(self, *args, **kwargs)
def get_fields():
return [(field.name, field.value_to_string(self)) for field in cls.fields]
and then apply it to any model class that you wish to have this functionality
#with_get_fields
class model():
def __init___(self):
...

Related

Iterate over Django custom Form

I am trying to create a dynamic form, with a varying number of CharFields. I want to be able to display them at will in semi-arbitrary places in my form. My approach was to create an iterable function that fielded the right self.fields[INDEX]. However, when I do this, I literally see this:
<django.forms.fields.CharField object at 0x80bae6be0>
<django.forms.fields.CharField object at 0x80bae6f98>
<django.forms.fields.CharField object at 0x80bae6da0>
How do I make a CharField() render as expected?
My code is below:
class ImplementationForm(forms.ModelForm):
"""
Specifies the implementation of a given control.
"""
class Meta:
model = Implementation
fields = ['implemented', 'scope', 'separate']
def __init__(self, *args, **kwargs):
control = kwargs.pop('control')
super(ImplementationForm, self).__init__(*args, **kwargs)
self.fields['separate'].widget.attrs.update({'class': 'separate'})
self.fields['scope'].widget.attrs.update({'class': 'scope'})
for substatement in control.substatement.all():
self.fields['statement_%s'%substatement.pk] = forms.CharField()
def subfield(self):
print("Comes herE")
for index in self.fields:
if index[:10] == 'statement_':
yield self.fields[index]
The template basically does this:
{% for x in myform.subfield %} {{ x }} {% endfor %}
What you are looking for is the form's BoundFields. e.g. {{ form.email }}
You are iterating over the Field instances (not the form's BoundField instances that wraps the Field Instances) e.g. {{ form.field.email }}.
This is why you are getting the
<django.forms.fields.CharField object at 0x80bae6da0>
result from your template. See: https://stackoverflow.com/a/671305/3035260
Also see django's documentation: https://docs.djangoproject.com/en/1.10/ref/forms/api/#django.forms.BoundField
Try this dirty way (See my edit below for a much better solution):
{# Iterate over your list of field instances #}
{% for x in myform.subfield %}
{# Find the matching BoundField instance #}
{% for field in myform %}
{% if field.field == x %}
{# Matching BoudField instance found, display it. #}
{{ field }}
{% endif %}
{% endfor %}
{% endfor %}
Edit:
Just came across a better (less dirty) approach:
A Field has a
get_bound_field(self, form, field_name)
method according to the docs: https://docs.djangoproject.com/en/1.10/_modules/django/forms/fields/#Field.get_bound_field
So, in your subfield method in the last line (The 'yield' line), try this:
yield self.fields[index].get_bound_field(self, index)
Then, your template will remain the same:
{% for x in myform.subfield %} {{ x }} {% endfor %}
and everything should work as you intended.

Django templates: why does __call__ magic method breaks the rendering of a non-model object?

Today I faced a strange issue on one of my development. I reproduced it with a very minimal example. Have a look at these 2 dummy classes (non Django model subclasses):
class DummyClassA(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Dummy1 object called ' + self.name
class DummyClassB(object):
"""Same as ClassA, with the __call__ method added"""
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Dummy2 object called ' + self.name
def __call__(self, *args, **kwargs):
return "bar"
They are identical, but the second have a special __call__() method.
I want to display instances of these 2 objects in a view using the builtin Django template engine:
class MyView(TemplateView):
template_name = 'myapp/home.html'
def get_context_data(self, **kwargs):
ctx = super(MyView, self).get_context_data(**kwargs)
list1 = [
DummyClassA(name="John"),
DummyClassA(name="Jack"),
]
list2 = [
DummyClassB(name="Albert"),
DummyClassB(name="Elmer"),
]
ctx.update({
'list1': list1,
'list2': list2,
})
return ctx
and the corresponding template:
<h1>Objects repr</h1>
<ul>
{% for element in list1 %}
<li>{{ element }}</li>
{% endfor %}
</ul>
<ul>
{% for element in list2 %}
<li>{{ element }}</li>
{% endfor %}
</ul>
<h1>Access members</h1>
<ul>
{% for element in list1 %}
<li>{{ element.name }}</li>
{% endfor %}
</ul>
<ul>
{% for element in list2 %}
<li>{{ element.name }}</li>
{% endfor %}
</ul>
I obtain this result:
When displaying instances of the second class ({{ element }}), the __call__ method is executed instead of __repr__(), and when I want to access a member of the class, it returns nothing.
I don't understand why defining the __call__() change the way Django template engine will handle those instances. I imagine this is not a bug but mostly a feature, but I am curious, why __call__() is run in such case. And why I can't get the value of element.name in the 2nd list ?
Because that's what the template language is designed to do. As the docs state:
If the resulting value [of looking up a variable] is callable, it is called with no arguments. The result of the call becomes the template value.
Without this, there would be no way of calling methods in templates, since the template syntax does not allow using parentheses.

Styling ModelMultipleChoiceField Django

I'm having trouble styling my django form with a ModelMultipleChoiceField.
Heres my form:
class SkriptenSelect(forms.Form):
skripten = StyledModelMultipleChoiceField(
queryset=None,
widget=forms.CheckboxSelectMultiple,
)
def __init__(self, *args, **kwargs):
choices = kwargs.pop('choices')
super(SkriptenSelect, self).__init__(*args, **kwargs)
self.fields['skripten'].queryset = choices
class StyledModelMultipleChoiceField(forms.ModelMultipleChoiceField):
def label_from_instance(self, obj):
return mark_safe('<ul class="list-inline" style="display: inline;">' \
'<li>{name}</li>' \
'<li>{semester}</li>' \
'<li>professor</li>' \
'</ul>'.format(name=escape(obj.name), semester=escape(obj.prefix))
)
And I used this for html:
<form action="." method="post">
<ul class="list-group">
{% for skript in result_form.skripten %}
<li class="list-group-item">
{{ skript }}
</li>
{% endfor %}
</ul>
{% csrf_token %}
<input type="submit" value="Submit Selected" />
This requires me to put my HTML in my forms file, which is not a very mvc way. Also it restricts me heavily in how i can style the list (i.e making table of the model instance fields)
Is there anyway to make this smarter? I'd like to access {{ skript.name }}, {{ skript.checkbox }} or something in my {% for skript in result_form.skripten %} loop, but thats sadly not possible...
You can use render_to_string. It loads a template and renders it with a context. Returns a string.
from django.template.loader import render_to_string
rendered = render_to_string('my_template.html', {'foo': 'bar'})
For various variants you can make various StyledModelMultipleChoiceField subclasses OR pass the desired template_name when initialising the class. Eg:
>>> class FooField:
... def __init__(self, template_name='default.html'):
... self.template_name = template_name
...
>>> x = FooField('table.html')
>>> x.template_name
'table.html'
Use self.template_name where appropriate:
def label_from_instance(self, obj)
return render_to_string(self.template_name, {'foo': obj.foo})
If you want to display multiple instances, use a formset.

Render form errors with the label rather than field name

I would like to list all form errors together using {{ form.errors }} in the template. This produces a list of form fields and nested lists of the errors for each field. However, the literal name of the field is used. The generated html with an error in a particular field might look like this.
<ul class="errorlist">
<li>
target_date_mdcy
<ul class="errorlist">
<li>This field is required.</li>
</ul>
</li>
</ul>
I would like use the errorlist feature, as it's nice and easy. However, I want to use the label ("Target Date", say) rather than the field name. Actually, I can't think of a case in which you would want the field name displaying for the user of a webpage. Is there way to use the rendered error list with the field label?
I don't see a simple way to do this.
The errors attribute of the form actually returns an ErrorDict, a class defined in django.forms.utils - it's a subclass of dict that knows to produce that ul rendering of itself as its unicode representation. But the keys are actually the field names, and that's important to maintain for other behavior. So it provides no easy access to the field labels.
You could define a custom template tag that accepts the form to produce the rendering you prefer, since in Python code it's easy to get the field label given the form and the field name. Or you could construct an error list by label in the view, add it to your context, and use that instead.
edit
Alternately again, you can iterate over the fields and check their individual errors, remembering to display non_field_errors as well. Something like:
<ul class="errorlist">
{% if form.non_field_errors %}
<li>{{ form.non_field_errors }}</li>
{% endif %}
{% for field in form %}
{% if field.errors %}
<li>
{{ field.label }}
<ul class="errorlist">
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
</ul>
You might want to wrap non_field_errors in a list as well, depending.
I know this has already been answered but, I ran across the same scenario and found there is a simple way to use the label:
{% if form.errors %}
<ul class="user-msg error">
{% for field in form %}
{% for error in field.errors %}
<li>
{% if field != '__all__' %}
<strong>{{ field.label }}:</strong>
{% endif %}
{{ error }}
</li>
{% endfor %}
{% endfor %}
</ul>
{% endif %}
I solved this in a custom form class which all my forms inherit instead of django.forms.Form. There I change the way form.errors works by returning a custom ErrorDict whose as_ul method takes labels into account. Thus you don't need to change your templates, but you need to have your forms inherit CustomBaseForm.
class CustomErrorDict(ErrorDict):
def __init__(self, form, iterable=None, **kwargs):
self.form = form
super(CustomErrorDict, self).__init__(iterable, **kwargs)
def as_ul(self):
if not self:
return u''
def humanify(field_name):
try:
return self.form.fields[field_name].label or field_name
except:
return field_name
# main logic is copied from the original ErrorDict:
return mark_safe(u'<ul class="errorlist">%s</ul>'
% ''.join([u'<li>%s%s</li>' % (humanify(k), force_unicode(v))
for k, v in self.items()]))
class CustomBaseForm(forms.Form):
#property
def errors(self):
return CustomErrorDict(self, super(forms.Form, self).errors)
... rest of CustomBaseForm ...
from django import forms
def my_clean(self):
self.my_errors = ''
for x in self.visible_fields():
if x.errors:
self.my_errors += "<p>%s: %s</p>" % (x.label, x.errors)
class SetPwdForm(forms.Form):
pwd= forms.CharField(label='password', required=True, min_length=6)
def clean(self):
...
my_clean(self)
use myform.my_errors in views.
Just in case anyone is looking do something like this using the django.contrib.messages framework in, for example, a FormView:
def form_invalid(self, form):
for field, errors in form.errors.items():
for error in errors:
messages.error(
self.request,
form.fields[field].label + ": " + error
)
Note this is just a basic template, you'll have to take care of non-field errors et cetera in your code in the case where form.fields[field] doesn't make sense.
The following approach shows verbose_name instead of the field name.
It can be used in get_context_data() too but personally, I prefer this way:
from django.core.exceptions import FieldDoesNotExist
class ShowVerboseNameInFormsMixin:
def add_error(self, field, error):
super(ShowVerboseNameInFormsMixin, self).add_error(field, error)
for field, message in self._errors.copy().items():
try:
verbose_name = self._meta.model._meta.get_field(field).verbose_name
del self._errors[field]
self._errors[verbose_name] = self.error_class()
self._errors[verbose_name].extend(message)
except FieldDoesNotExist:
pass
and then use it like this:
from django import forms
class FooForm(ShowVerboseNameInFormsMixin, forms.ModelForm):
class Meta:
model = Foo
fields = ['foo', 'bar', 'baz']
with a little extra code, It can show the __all__ to all or any other intended string.
Here's the filter I used to render an error list with the field label, following Peter's suggestion.
from django.utils.safestring import mark_safe
from django.template import Library, Context, loader
register = Library()
#register.filter
def form_error_list(form):
result = ""
if form.errors:
fields = form.fields
error_items = []
for error_field in form.errors:
label = fields[error_field].label
if not label:
label = ' '.join([word.capitalize() for word in error_field.split('_')])
errors = form.errors[error_field]
error_li = ''.join(['<li>{0}</li>'.format(error) for error in errors])
error_items.append({'label': label, 'error_li': error_li})
inner = ''.join(['<li>{0}<ul class="errorlist">{1}</ul></li>'.format(item['label'], item['error_li']) for item in error_items])
result = mark_safe('<ul class="errorlist">{0}</ul>'.format(inner))
return result

How can I add custom properties to a field class for output in template?

I would like to add a custom field which can provide additional properties for handling the individual output of in a template. For instance here's my class:
class CustomField(CharField):
suffix = "things"
And my accompanying field.html where I've added the field.suffix block:
<div class="controlGroup clearfix {% if field.errors %}error{% endif %}">
<label class="controlLabel" for="{{ field.auto_id }}">{{ field.label }}</label>
<div class="controls">
{{ field }}
{% if field.suffix %}
<span class="suffix">{{ field.suffix }}</span>
{% endif %}
{% if field.errors %}
<span class="helpInline">{{ field.errors }}</span>
{% endif %}
</div>
</div>
Unfortunately the suffix block simply doesn't output.
EDIT: To clarify, both current answers below are targeted at adding widget attributes. I'm actually trying to add a custom string for use in the template, not a custom CSS class.
For your simple example, you can override the attrs dictionary of the widget that you use in for your form field, you don't need to create a custom field.
class YourForm(forms.Form):
some_field = forms.CharField(widget=forms.TextInput(attrs={'class': 'suffix'})
If you really want to create a custom field, you need to create a custom widget and control the render method. Depending in what type of form control you want to render, you inherit your custom widget from the equivalent provided by django.
Attributes can be passed to form fields by two ways. I have given the example code below.
class ArticleForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
'field1' : forms.TextInput(attrs = {'class': 'suffix'}),
'field2' : forms.TextInput(attrs = {'class': 'suffix'}),
}
OR
class ArticleForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kw):
super(forms.ModelForm, self).__init__(*args, **kw)
self.fields['field1'].widget.attrs['class'] = 'suffix'
self.fields['field2'].widget.attrs['class'] = 'suffix'
You can use widgets as given in first example or in init as given in second example. Both will work.
EDIT: Any attribute can be passed to the django form field using the above example.
I eventually did this by creating my own widget:
class UsernameInput(TextInput):
"""
Creates a widget specifically for appending the username suffix as a label
""" 9
def render(self, name, value, attrs=None):
widget = super(UsernameInput, self).render(name, value, attrs)
widget += u'<span class="field-suffix">#%s</span>' % USERNAME_DOMAIN_PART
return mark_safe(widget)
And then in my signup form by specifying the widget like so:
username = forms.EmailField(label="Preferred Username",
widget=UsernameInput(),
error_messages={'invalid': "Please enter a valid email address"})

Categories