问题
Is there a way in Django to add custom attributes to a model's fields (without resorting to subclassing fields)?
I would like to only display certain fields in certain sections of my template. This is because eventually each type of field will be displayed in a separate tab. I thought about adding a custom attribute to each field to identify which section/tab it should go in. But, so far, I've had no luck.
I have a few field types:
class Enum(set):
def __getattr__(self, name):
if name in self:
return name
raise AttributeError
FieldTypes = Enum(["one","two","three",])
And a few models:
class Model1(models.Model):
a = models.CharField()
b = models.ForeignKey('Model2')
c = models.IntegerField()
a.type = FieldTypes.one # this obviously doesn't work
b.type = FieldTypes.two # this obviously doesn't work
c.type = FieldTypes.three # this obviously doesn't work
class Model2(models.Model):
d = models.CharField()
And a form:
class Form1(forms.ModelForm):
class Meta:
model = Mode1
And a template:
{% for fieldType in FieldTypes %}
<div class="{{fieldType}}">
{% for field in form %}
{% if field.type = fieldType %}
{{ field }}
{% endif %}
{% endfor %}
</div>
{% endfor %}
But this doesn't work.
Ideas? Or other suggestions for only placing certain fields in certain sections of the page?
Thanks.
回答1:
In general, I would keep this logic outside of the model class. Models shouldn't be tangled up with presentation elements if you can help it, and choosing which fields to display in a form seems like a presentation concern. Fortunately, the Form class provides a nice, UI-focused layer between the data layer (the model) and the presentation layer (the view and template).
Here's how I've addressed this in the past. In my Form class, I created a list of field groups, each with a title and a list of the names of the fields they contain:
class MyModelForm(forms.ModelForm):
field_groups = (
{'name':'Group One', 'fields':('a', 'b', 'c')},
{'name':'Group Two', 'fields':('d', 'e')},
)
class Meta:
model = MyModel
Then in the template, I looped through the groups, and within that loop conditionally included those fields:
{% for group in form.field_groups %}
<h3 class="groupheader">{{group.name}}</h3>
{% for field in form %}
{% if field.name in group.fields %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }}: {{ field }}
</div>
{% endif %}
{% endfor %}
{% endfor %}
This allows you to control the grouping and display of form fields within the MyModelForm
class, which is a reasonable place for presentation logic to live.
回答2:
It's posible!
class Model1(models.Model):
a = models.CharField()
b = models.ForeignKey('Model2')
c = models.IntegerField()
And a form:
class Form1(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(Form1, self).__init__(*args, **kwargs)
self.fields['a'].type = FieldTypes.one # this obviously doesn't work
self.fields['b'].type = FieldTypes.two # this obviously doesn't work
self.fields['c'].type = FieldTypes.three # this obviously doesn't work
class Meta:
model = Mode1
回答3:
Turns out, that I do want this logic in my model class; the field types are used for more than just working out where/how to display them (though they are used for that too). I have come up with the following solution.
I defined some classes to store a set of field types:
class FieldType(object):
def __init__(self, type=None, name=None):
self._type = type
self._name = name
def getType(self):
return self._type
def getName(self):
return self._name
class FieldTypeList(deque):
def __getattr__(self,type):
for ft in self:
if ft.getType() == type:
return ft
raise AttributeError
FieldTypes = FieldTypeList([FieldType("ONE","The Name Of Field One"),FieldType("TWO","The Name Of Field Two")])
And a few models each of which has a set of mappings from field types to particular field names (In this example, fields 'a', 'b', and 'c' are of type 'ONE' and field 'd' is of type 'TWO'):
class ParentModel(models.Model):
_fieldsByType = {}
a = models.CharField()
b = models.CharField()
def __init__(self, *args, **kwargs):
super(ParentModel, self).__init__(*args, **kwargs)
for ft in FieldTypes:
self.setFieldsOfType(ft, [])
self.setFieldsOfType(FieldTypes.ONE, ['a','b'])
def setFieldsOfType(self,type,fields):
typeKey = type.getType()
if typeKey in self._fieldsByType:
self._fieldsByType[typeKey] += fields
else:
self._fieldsByType[typeKey] = fields
class ChildModel(ParentModel):
_fieldsByType = {} # not really sure why I have to repeat this attribute in the derived class
c = models.CharField()
d = models.CharField()
def __init__(self, *args, **kwargs):
super(ChildModel, self).__init__(*args, **kwargs)
self.setFieldsOfType(FieldTypes. ['c'])
self.setFieldsOfType(FieldTypes. ['d'])
I have a basic form:
class MyForm(forms.ModelForm):
class Meta:
model = ChildModel
And a custom filter to return all fields of a given type from a particular form (note, the accessing the model from the form via its Meta class):
@register.filter
def getFieldsOfType(form,type):
return form.Meta.model._fieldsByType[type.getType()]
And, finally, a template to pull it all together (the template takes MyForm and FieldTypes):
{% for type in types %}
<div id="{{type.getType}}">
{% with fieldsOfType=form|getFieldsOfType:type %}
{% for field in form %}
{% if field.name in fieldsOfType %}
<p>
{{ field.errors }}
{{ field.label_tag }}: {{ field }}
</p>
{% endif %}
{% endfor %}
{% endwith %}
</div>
{% endfor %}
来源:https://stackoverflow.com/questions/8407837/django-custom-attributes-for-model-fields