我现在已经在这一个上刮了一会儿。我正在尝试构建一个自定义模型字段来存储邮政地址。
一般的想法是在models.TextField
中序列化后存储所有数据,并使用form.MultiValueField
来捕获每个值。
出于某种原因,即使迁移工作正常,在管理员中,该字段也会显示为简单的TextField
。好像MultiValueField
部分被完全忽略了......
这是我的代码:
from django.db import models
from django.forms import MultiValueField, CharField
from django.utils.translation import ugettext_lazy as _
class Address(object):
"""A postal address."""
def __init__(self, street, postal_code, city,
country, complement=None, region=None):
self.street = street
self.complement = complement
self.postal_code = postal_code
self.city = city
self.region = region
self.country = country
def print_address_inline(self):
complement = ''
region = ''
if self.complement:
complement = ', %s' % self.complement
if self.region:
region = ', %s' % self.region
data = {
'street': self.street,
'complement': complement,
'code': self.postal_code,
'city': self.city,
'region': region,
'country': self.country
}
return '%(street)s%(complement)s, %(code)s %(city)s%(region)s' \
', %(country)s' % data
def __str__(self):
return self.print_address_inline()
class AddressFormField(MultiValueField):
def __init__(self, *args, **kwargs):
del kwargs['max_length']
error_messages = {
'incomplete': _('Enter a complete address: ' \
'street, postal code, city and country.'),
}
fields = (
CharField(
label='street', max_length=1024,
error_messages={
'incomplete': _('Enter the number and street.')
}
),
CharField(
label='complement', max_length=1024, required=False
),
CharField(
label='code', max_length=10,
error_messages={'incomplete': _('Enter the postal code.')}
),
CharField(
label='city', max_length=255,
error_messages={'incomplete': _('Enter the city.')}
),
CharField(
label='region', max_length=255, required=False
),
CharField(
label='country', max_length=255,
error_messages={'incomplete': _('Enter the country.')}
)
)
super(AddressFormField, self).__init__(
error_messages=error_messages, fields=fields,
require_all_fields=False, *args, **kwargs
)
def compress(self, data_list):
if data_list:
if data_list[0] in self.empty_values:
raise ValidationError(
_('Enter the number and street.'),
code='incomplete'
)
if data_list[2] in self.empty_values:
raise ValidationError(
_('Enter the postal code.'),
code='incomplete'
)
if data_list[3] in self.empty_values:
raise ValidationError(
_('Enter the city.'),
code='incomplete'
)
if data_list[5] in self.empty_values:
raise ValidationError(
_('Enter the country.'),
code='incomplete'
)
address = Address(
street=data_list[0],
complement=data_list[1],
code=data_list[2],
city=data_list[3].title(),
region=data_list[4],
country=data_list[5].upper()
)
return address
return None
class AddressField(models.TextField):
description = "A postal address."
def __init__(self, *args, **kwargs):
super(AddressField, self).__init__(*args, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super(AddressField, self).deconstruct()
return name, path, args, kwargs
def from_db_value(self, value, expression, connection, context):
if value is None:
return value
data = value.split('@@')
return Address(*data)
def to_python(self, value):
if isinstance(value, Address):
return value
if value is None:
return value
data = value.split('@@')
return Address(*data)
def get_prep_value(self, value):
if isinstance(value, Address):
data = []
for v in [
value.street,
value.complement,
value.postal_code,
value.city,
value.region,
value.country,
]:
if v is not None:
data.append(v)
else:
data.append('')
return '@@'.join(data)
else:
return value
def formfield(self, **kwargs):
defaults = {'form_class': AddressFormField}
defaults.update(kwargs)
return super(AddressField, self).formfield(**defaults)
如果你有任何线索......
谢谢!
答案 0 :(得分:0)
我终于完成了this piece of code。
from django.db import models
from django.forms import MultiValueField, CharField, TextInput
from django.forms.widgets import MultiWidget
from django.utils.translation import ugettext_lazy as _
class Address(object):
"""A postal address."""
def __init__(self, street, complement, postal_code, city, region, country):
self.street = street
self.complement = complement
self.postal_code = postal_code
self.city = city
self.region = region
self.country = country
def print_address_inline(self):
complement = ''
region = ''
if self.complement != '':
complement = ', %s' % self.complement
if self.region != '':
region = ', %s' % self.region
data = {
'street': self.street,
'complement': complement,
'code': self.postal_code,
'city': self.city,
'region': region,
'country': self.country
}
return '%(street)s%(complement)s, %(code)s %(city)s%(region)s' \
', %(country)s' % data
def get_values_list(self):
data = [
self.street,
self.complement,
self.postal_code,
self.city,
self.region,
self.country,
]
value_list = []
for value in data:
if value is not None:
value_list.append(value)
else:
value_list.append('')
return value_list
def __str__(self):
return self.print_address_inline()
class AddressWidget(MultiWidget):
"""A special widget to render Address form field."""
def __init__(self, attrs=None):
# TODO Look for django-localflavor to improve this custom field.
# https://django-localflavor.readthedocs.io/en/latest/localflavor/fr/
widgets = [
TextInput(attrs={'placeholder':'street'}),
TextInput(attrs={'placeholder':'complement'}),
TextInput(attrs={'placeholder':'postal code'}),
TextInput(attrs={'placeholder':'city'}),
TextInput(attrs={'placeholder':'region'}),
TextInput(attrs={'placeholder':'country'}),
]
super(AddressWidget, self).__init__(widgets, attrs)
def decompress(self, value):
if value:
return [
value.street,
value.complement,
value.postal_code,
value.city,
value.region,
value.country,
]
return [None, None, None, None, None, None]
class AddressFormField(MultiValueField):
"""A special form field to handle Address model field."""
widget = AddressWidget
def __init__(self, *args, **kwargs):
error_messages = {
'incomplete': _('Enter a complete address: ' \
'street, postal code, city and country.'),
}
fields = (
CharField(
label='street', max_length=1024,
error_messages={
'incomplete': _('Enter the number and street.')
}
),
CharField(
label='complement', max_length=1024, required=False
),
CharField(
label='code', max_length=10,
error_messages={'incomplete': _('Enter the postal code.')}
),
CharField(
label='city', max_length=255,
error_messages={'incomplete': _('Enter the city.')}
),
CharField(
label='region', max_length=255, required=False
),
CharField(
label='country', max_length=255,
error_messages={'incomplete': _('Enter the country.')}
)
)
super(AddressFormField, self).__init__(
error_messages=error_messages, fields=fields,
require_all_fields=False, *args, **kwargs
)
def compress(self, data_list):
if data_list:
if data_list[0] in self.empty_values:
raise ValidationError(
_('Enter the number and street.'),
code='incomplete'
)
if data_list[2] in self.empty_values:
raise ValidationError(
_('Enter the postal code.'),
code='incomplete'
)
if data_list[3] in self.empty_values:
raise ValidationError(
_('Enter the city.'),
code='incomplete'
)
if data_list[5] in self.empty_values:
raise ValidationError(
_('Enter the country.'),
code='incomplete'
)
address = Address(
street=data_list[0],
complement=data_list[1],
postal_code=data_list[2],
city=data_list[3].title(),
region=data_list[4],
country=data_list[5].upper()
)
return address
return None
class AddressField(models.Field):
description = "A postal address."
def __init__(self, *args, **kwargs):
super(AddressField, self).__init__(*args, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super(AddressField, self).deconstruct()
return name, path, args, kwargs
def from_db_value(self, value, expression, connection, context):
if value is None or value == '':
return value
data = value.split('@@')
address = Address(*data)
return address
def to_python(self, value):
if isinstance(value, Address):
return value
if value is None:
return value
data = value.split('@@')
return Address(*data)
def get_prep_value(self, value):
if value:
return '@@'.join(value.get_values_list())
return ''
def get_internal_type(self):
return 'TextField'
def formfield(self, **kwargs):
defaults = {'form_class': AddressFormField}
defaults.update(kwargs)
return super(AddressField, self).formfield(**defaults)
随时发表评论或分享您的想法。 我选择使用双'@'作为分隔符。这是有争议的,但它适用于我的情况。