通过 modelforms 将非规范化数据导入 Django 模型
Importing Denormalized data into django models via modelforms
场景:
我有一些数据看起来有点像这样:
Person | Favorite Color | Favorite Fruit
------------------------------------------
Bobby | RED | BANANA
Jared | YELLOW | RASPBERRY
Milly | BLACK | PEACH
Shawn | ORANGE | ORANGE
假设它是一个平面文件,或者 python 字典,或者其他一些非 sql 格式。
编辑:假设我已经在一个 Python 结构中得到它,如下所示:
data = [
{"name": "Bobby", "favorite_color": "RED", "favorite_fruit": "BANANA"},
{"name": "Jared", "favorite_color": "YELLOW", "favorite_fruit": "RASPBERRY"},
# etc....
]
我有如下所示的 Django 模型:
class Person(models.Model):
COLORS = (
('R', 'RED'),
('O', 'ORANGE'),
('Y', 'YELLOW'),
('G', 'GREEN'),
('B', 'BLUE'),
('P', 'PURPLE'),
('L', 'BLACK'),
('W', 'WHITE')
)
name = CharField(max_length=256)
favorite_color = CharField(max_length=1, choices=COLORS)
favorite_fruit = ForeignKey(Fruit)
class Fruit(models.Model):
name = CharField(max_length=256)
fructose_content = PositiveIntegerField()
编辑:假设我的 Fruit
模型已经填充了所有可能的水果。
任务:
我想使用 ModelForm
s 将我的数据从原始源导入我的 Django 模型,以利用适当的验证和数据库抽象。
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = '__all__'
有没有办法 ModelForm
可以将非规范化数据转换为可以保存在模型中的数据? ModelForm
这里用错了吗?
试试下面的代码:
insert_data = []
with open('data.txt') as f:
state = 'HEADER'
headers = []
for line in f.readlines():
if state == 'HEADER':
headers = [header.lower().strip().replace(' ', '_') for header in line.split('|')]
state = 'IGNORE'
elif state == 'IGNORE':
state = 'DATA'
elif state == 'DATA':
data_values = map(str.strip, line.split('|'))
insert_entry = {}
for key, data in zip(headers, data_values):
insert_entry[key] = data
insert_data.append(insert_entry)
for row in insert_data:
form = PersonForm(row)
if form.is_valid():
form.save()
else:
print form.errors()
第一步是读取文件(假设文件名为 data.txt),我建议您使用 json 或其他一些结构化文本以避免输入错误,因为您可以先检查是否文件使用众所周知的库进行了良好格式化。
要使此脚本正常工作,您还需要对表单中的字段使用一些技巧,我认为将 PERSON 字段称为 NAME 就足够了。
第二步,我们为每个要插入的条目创建表单实例,验证它们,如果一切正常,我们将它们保存到数据库中。
希望对您有所帮助,
我想出了一个部分解决方案,至少对于涉及选择的问题。我想通过一些修补它也可以用于 ForeignKey
字段。
首先,我定义了一个函数 get_choice_by_name
,它遍历一个选择元组并按值查找键。
然后我将 TypedChoiceField
子类化并覆盖其 clean()
方法来转换数据。此方法似乎在任何验证之前被调用。
代码如下:
def get_choice_by_name(name, choices, case_sensitive=False):
try:
if name is None:
return ''
elif name and not case_sensitive:
return next(k for k, n in choices
if n.lower() == name.lower())
else:
return next(k for k, n in choices if n == name)
except StopIteration:
raise ValueError(
"Invalid choice: {}, not found in {}".format(name, choices)
)
class DenormalizedChoiceField(TypedChoiceField):
def clean(self, value):
if not value:
return self.empty_value
try:
value = get_choice_by_name(value, self.choices)
except ValueError as e:
raise ValidationError(str(e))
value = super(DenormalizedChoiceField, self).clean(value)
return value
我的 ModelForm
现在只需要将有问题的字段重新定义为 DenormalizedChoiceField
。不过,我需要明确指定选项,但出于某种原因,如果您覆盖该字段,它不会从模型中选择它。
class PersonForm(forms.ModelForm):
favorite_color = DenormalizedChoiceField(choices=Person.COLORS)
class Meta:
model = Person
fields = '__all__'
场景:
我有一些数据看起来有点像这样:
Person | Favorite Color | Favorite Fruit
------------------------------------------
Bobby | RED | BANANA
Jared | YELLOW | RASPBERRY
Milly | BLACK | PEACH
Shawn | ORANGE | ORANGE
假设它是一个平面文件,或者 python 字典,或者其他一些非 sql 格式。
编辑:假设我已经在一个 Python 结构中得到它,如下所示:
data = [
{"name": "Bobby", "favorite_color": "RED", "favorite_fruit": "BANANA"},
{"name": "Jared", "favorite_color": "YELLOW", "favorite_fruit": "RASPBERRY"},
# etc....
]
我有如下所示的 Django 模型:
class Person(models.Model):
COLORS = (
('R', 'RED'),
('O', 'ORANGE'),
('Y', 'YELLOW'),
('G', 'GREEN'),
('B', 'BLUE'),
('P', 'PURPLE'),
('L', 'BLACK'),
('W', 'WHITE')
)
name = CharField(max_length=256)
favorite_color = CharField(max_length=1, choices=COLORS)
favorite_fruit = ForeignKey(Fruit)
class Fruit(models.Model):
name = CharField(max_length=256)
fructose_content = PositiveIntegerField()
编辑:假设我的 Fruit
模型已经填充了所有可能的水果。
任务:
我想使用 ModelForm
s 将我的数据从原始源导入我的 Django 模型,以利用适当的验证和数据库抽象。
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = '__all__'
有没有办法 ModelForm
可以将非规范化数据转换为可以保存在模型中的数据? ModelForm
这里用错了吗?
试试下面的代码:
insert_data = []
with open('data.txt') as f:
state = 'HEADER'
headers = []
for line in f.readlines():
if state == 'HEADER':
headers = [header.lower().strip().replace(' ', '_') for header in line.split('|')]
state = 'IGNORE'
elif state == 'IGNORE':
state = 'DATA'
elif state == 'DATA':
data_values = map(str.strip, line.split('|'))
insert_entry = {}
for key, data in zip(headers, data_values):
insert_entry[key] = data
insert_data.append(insert_entry)
for row in insert_data:
form = PersonForm(row)
if form.is_valid():
form.save()
else:
print form.errors()
第一步是读取文件(假设文件名为 data.txt),我建议您使用 json 或其他一些结构化文本以避免输入错误,因为您可以先检查是否文件使用众所周知的库进行了良好格式化。
要使此脚本正常工作,您还需要对表单中的字段使用一些技巧,我认为将 PERSON 字段称为 NAME 就足够了。
第二步,我们为每个要插入的条目创建表单实例,验证它们,如果一切正常,我们将它们保存到数据库中。
希望对您有所帮助,
我想出了一个部分解决方案,至少对于涉及选择的问题。我想通过一些修补它也可以用于 ForeignKey
字段。
首先,我定义了一个函数 get_choice_by_name
,它遍历一个选择元组并按值查找键。
然后我将 TypedChoiceField
子类化并覆盖其 clean()
方法来转换数据。此方法似乎在任何验证之前被调用。
代码如下:
def get_choice_by_name(name, choices, case_sensitive=False):
try:
if name is None:
return ''
elif name and not case_sensitive:
return next(k for k, n in choices
if n.lower() == name.lower())
else:
return next(k for k, n in choices if n == name)
except StopIteration:
raise ValueError(
"Invalid choice: {}, not found in {}".format(name, choices)
)
class DenormalizedChoiceField(TypedChoiceField):
def clean(self, value):
if not value:
return self.empty_value
try:
value = get_choice_by_name(value, self.choices)
except ValueError as e:
raise ValidationError(str(e))
value = super(DenormalizedChoiceField, self).clean(value)
return value
我的 ModelForm
现在只需要将有问题的字段重新定义为 DenormalizedChoiceField
。不过,我需要明确指定选项,但出于某种原因,如果您覆盖该字段,它不会从模型中选择它。
class PersonForm(forms.ModelForm):
favorite_color = DenormalizedChoiceField(choices=Person.COLORS)
class Meta:
model = Person
fields = '__all__'