如何在Django中用ManyToManyFields模型的对象填充测试数据库?

xzlaal3s  于 2023-02-17  发布在  Go
关注(0)|答案(1)|浏览(159)

我想测试我的项目的速度,所以我需要用测试数据填充我的数据库。
我有一个模型有很多ManyToManyField(稍后我会创建更多的模型):

class Deciduous(PlantBasicCharacteristics):
    usda_zone = models.ManyToManyField(UsdaZone)
    soil_moisture = models.ManyToManyField(SoilMoisture)
    soil_fertility = models.ManyToManyField(SoilFertility)
    soil_ph = models.ManyToManyField(SoilPh)

我正在尝试创建一个实用程序,用测试数据填充DB:

from random import randint

from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from django.db.models.fields import CharField, DecimalField
from django.db.models.fields.related import ManyToManyField
from django.shortcuts import get_object_or_404

from plants.models import Deciduous
from plants.web_page_filter_fields import get_plant_class_fields

PLANT_CLASSES = [Deciduous, ]
TEST_DATA_AMOUNT = 3

class Command(BaseCommand):

    def handle(self, *args, **options):
        for plant_class in PLANT_CLASSES:
            fields_and_names = get_plant_class_fields(plant_class)
            data = []
            for _ in range(TEST_DATA_AMOUNT):
                data.append(self.create_data(fields_and_names))
            print(data)
            plant_class.objects.bulk_create([
                plant_class(**values) for values in data
            ])

    def create_data(self, fields_and_names):
        data = {}
        for field_name, field_model in fields_and_names.items():
            if ('ptr' not in field_name
                    and 'synonym' not in field_name
                    and field_name != 'id'):
                if isinstance(field_model, CharField):
                    data[field_name] = self.get_string_random()
                elif isinstance(field_model, DecimalField):
                    data[field_name] = self.get_number_random()
                elif isinstance(field_model, ManyToManyField):
                    data[field_name] = [self.get_choice_random(field_model)]
        return data

    def get_string_random(self):
        letters = [chr(randint(97, 122)) for _ in range(randint(5, 20))]
        return ''.join(letters).capitalize()

    def get_number_random(self):
        return randint(10, 15000) / 100

    def get_choice_random(self, model):
        field_model = model.related_model
        field_choices = field_model._meta.get_field('name').choices
        choice_number = randint(0, len(field_choices) - 1)
        choice = field_choices[choice_number][0]
        return get_object_or_404(field_model, name=choice)

但我得到:

File "manage.py", line 22, in <module>
    main()
  File "manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.7/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/base.py", line 354, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
  File "/app/plants/management/commands/add_test_data.py", line 30, in handle
    plant_class(**values) for values in data
  File "/app/plants/management/commands/add_test_data.py", line 30, in <listcomp>
    plant_class(**values) for values in data
  File "/usr/local/lib/python3.7/site-packages/django/db/models/base.py", line 498, in __init__
    _setattr(self, prop, kwargs[prop])
  File "/usr/local/lib/python3.7/site-packages/django/db/models/fields/related_descriptors.py", line 547, in __set__
    % self._get_set_deprecation_msg_params(),
TypeError: Direct assignment to the forward side of a many-to-many set is prohibited. Use usda_zone.set() instead.

是否可以使用带有set()的for循环创建对象,而不必为每个ManyToManyField编写代码?

sq1bmfud

sq1bmfud1#

尝试使用set()方法为ManyToManyFields创建对象,以便:

def create_data(self, fields_and_names):
    data = {}
    for field_name, field_model in fields_and_names.items():
        if ('ptr' not in field_name
                and 'synonym' not in field_name
                and field_name != 'id'):
            if isinstance(field_model, CharField):
                data[field_name] = self.get_string_random()
            elif isinstance(field_model, DecimalField):
                data[field_name] = self.get_number_random()
            elif isinstance(field_model, ManyToManyField):
                choices = field_model.related_model.objects.all()
                data[field_name] = set(choices.order_by('?')[:randint(1, len(choices))])
    return data

这里,order_by('?')用于随机地对选择进行排序,并且使用分片来选择随机数目的选择。

编辑

试试这个:

from random import sample

# ...

if isinstance(field_model, ManyToManyField):
    choices = field_model.related_model.objects.all()
    count = randint(1, len(choices))
    data[field_name] = set(sample(choices, count))

编辑2

尝试首先创建不具有多对多关系的对象,然后使用set方法添加相关对象,以便:

def create_data(self, fields_and_names):
    data = {}
    many_to_many_fields = []
    for field_name, field_model in fields_and_names.items():
        if ('ptr' not in field_name
                and 'synonym' not in field_name
                and field_name != 'id'):
            if isinstance(field_model, CharField):
                data[field_name] = self.get_string_random()
            elif isinstance(field_model, DecimalField):
                data[field_name] = self.get_number_random()
            elif isinstance(field_model, ManyToManyField):
                many_to_many_fields.append(field_name)
    obj = Deciduous.objects.create(**data)
    for field_name in many_to_many_fields:
        choices = [self.get_choice_random(Deciduous._meta.get_field(field_name)) for _ in range(randint(1, 3))]
        getattr(obj, field_name).set(choices)
    return data

相关问题