简体   繁体   English

通过模型形式将非规范化数据导入Django模型

[英]Importing Denormalized data into django models via modelforms

The Scenario: 场景:

I have some data that looks a bit like this: 我有一些看起来像这样的数据:

Person   | Favorite Color | Favorite Fruit
------------------------------------------
Bobby    | RED            | BANANA
Jared    | YELLOW         | RASPBERRY
Milly    | BLACK          | PEACH
Shawn    | ORANGE         | ORANGE

Assume it's in a flatfile, or python dicts, or some other non-sql format. 假设它是一个平面文件,python dicts或其他一些非sql格式。

EDIT: Assume for the sake of argument that I've already got it in a Python structure that looks like this: 编辑:为了论证,假设我已经在看起来像这样的Python结构中获取了它:

data = [
    {"name": "Bobby", "favorite_color": "RED", "favorite_fruit": "BANANA"},
    {"name": "Jared", "favorite_color": "YELLOW", "favorite_fruit": "RASPBERRY"},
    # etc....
 ]

I have django models that look like this: 我有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()

EDIT: Assume that my Fruit model is already populated with all the possible fruits. 编辑:假设我的Fruit模型已经填充了所有可能的水果。

The task: 任务:

I would like to import my data from the original source into my Django models by using ModelForm s, to take advantage of proper validation and database abstraction. 我想使用ModelForm将数据从原始源导入到Django模型中,以利用适当的验证和数据库抽象。

class PersonForm(forms.ModelForm):
    class Meta:
        model = Person
        fields = '__all__'

Is there a way the ModelForm can translate the denormalized data into data that can be saved in the model? ModelForm可以将非规范化数据转换为可以保存在模型中的数据? Are ModelForm s the wrong thing to use here? 在这里使用ModelForm是错误的事情吗?

Try the following code: 尝试以下代码:

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()

The first step is to read the file (assuming it is named data.txt), I recommend you using json or some other structured text to avoid typing errors as you can check first if the file is well formatted using well-known libraries. 第一步是读取文件(假设文件名为data.txt),我建议您使用json或其他一些结构化文本以避免输入错误,因为您可以首先检查文件是否已使用知名库进行了正确格式化。

For this script to work, you will also need a trick for the fields in your forms, I think it will be enough calling the PERSON field as NAME. 为了使此脚本正常工作,您还需要技巧来填写表单中的字段,我认为将PERSON字段称为NAME就足够了。

On the second step we create the forms instances for every entry we want to insert, validate them and if everything is OK, we save them into the database. 在第二步中,我们为要插入的每个条目创建表单实例,验证它们,如果一切正常,则将它们保存到数据库中。

Hope it helps, 希望能帮助到你,

I came up with a partial solution, at least for the problem involving the choices. 我提出了部分解决方案,至少是针对涉及选择的问题。 I guess with some tinkering it could work for ForeignKey fields as well. 我猜想也许它也可以适用于ForeignKey字段。

First, I define a function get_choice_by_name which goes through a choices tuple and looks for a key by value. 首先,我定义一个函数get_choice_by_name ,该函数经历一个选择元组并按值查找键。

Then I subclassed TypedChoiceField and overrode its clean() method to transform the data. 然后,我将TypedChoiceField子类TypedChoiceField并覆盖其clean()方法以转换数据。 This method seems to get called before any validation. 似乎在进行任何验证之前都会调用此方法。

Here's the code: 这是代码:

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

My ModelForm now just needs to redefine the fields in question as DenormalizedChoiceField . ModelForm现在只需要重新定义字段问题作为DenormalizedChoiceField I need to specify the choices explicitly, though, for some reason it doesn't pick this up from the model if you override the field. 但是,由于某些原因,如果您覆盖该字段,它不会从模型中选择,所以我需要明确指定选择。

class PersonForm(forms.ModelForm):
    favorite_color = DenormalizedChoiceField(choices=Person.COLORS)
    class Meta:
        model = Person
        fields = '__all__'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM