views:

36

answers:

2

What Django Does

Django's Model Field "blank" attribute, and the way it gets negated and turned into the Form Field "required" attribute when you create a ModelForm, is pretty cool. It allows me to set the property on the model where it belongs, but have the "required" attribute available when handling a ModelForm created from it. Having "required" available when rendering in a template means I can do cool things like add an asterisk to the display labels of required fields.

Model

class Employee(models.Model):
    name = models.CharField(blank=False)
    public_bio = models.TextField(blank=False)
    salary= models.DecimalField(max_digits=15, decimal_places=2, blank=False)
    personnel_notes = models.TextField(blank=True)

Template (using ModelForm)

{% for field in form %}
    <p>
       {{ field.label }}{% if field.required %}*{% endif %}
       {{ field }}
    </p>
{% endfor %}

What I Want It to Do

But what if I want to do that with my own, new attribute? It doesn't need to be negated/translated the way blank --> required does, but I want my new attribute, defined on the Model Field, to be accessible on my FormFields. For example, let's say I want to make explicit to the user which fields might be published:

Model

class Employee(models.Model):
    name = models.CharField(blank=False, publishable=True)
    public_bio = models.TextField(blank=False, publishable=True)
    salary= models.DecimalField(max_digits=15, decimal_places=2, blank=False, publishable=False)
    personnel_notes = models.TextField(blank=True, publishable=False)

Template (using ModelForm)

{% for field in form %}
    <p>
       {{ field.label }}{% if field.required %}*{% endif %}
       {% if field.publishable %}(may be published){% endif %}
       {{ field }}
    </p>
{% endfor %}

Is there any way to do this? Trying it, I'm running into an issue immediately with the model definition, where django.db.models.fields.Field.__init__() doesn't even accept kwargs. If this isn't possible, any ideas for workarounds to get the desired behavior?

+1  A: 

Note that publishable should be a property on the forms.Field, not on the models.Field, so that it will appear in the template.

You can add this explicitly on the fields you wish to have publishable in the form's initiation, and it will be available to you while rendering:

class PublishableForm(forms.Form):
    name = forms.CharField()

    def __init__(*args, **kwargs)
       super(PublishableForm, self).__init__(*args, **kwargs)
       self.name.publishable = True
OmerGertel
So then I'd have to set it manually on the form for each field from the model - and so the definition of those fields would effectively be split between two places: some of it on the Model, and some on the Form. What I really need is some way to get this information onto the model fields - then I could conceivably use your method to pull that data and put it on the form fields.
modulatrix
Two options: inherit the fields (PublishablePositiveInt), which isn't great, or add a new Publishable model to hold this information for you (model_name, field_name, publishable boolean).
OmerGertel
Ended up using this, with some tweaks to make it dynamic - along with creating a new `publishable_fields` attribute on the model class that contains a list of the publishable fieldnames. Not as clean as being able to set that property on each individual model field, but it at least keeps the definition in the model.
modulatrix
That's actually not a bad compromise. Thanks for sharing.
OmerGertel
A: 

You can also make kind of decorator for model fields:

def publishable(field):
    field.publishable = True
    return field

#...

name = publishable(models.CharField(...))

Then override form's __init__ to use these fields.

Also don't forget that using {{ form.name }} in template returns BoundField. To get original form field you should use {{ form.name.field }}.

Vladimir Sidorenko