【问题标题】:Extra fields on many-to-many relationships: How to automatically create the "through" fields?多对多关系上的额外字段:如何自动创建“通过”字段?
【发布时间】:2022-10-25 15:50:06
【问题描述】:

我正在尝试使用多对多字段来为潜在客户添加一组设施。 问题是我需要一个额外的字段来显示为我添加到线索中的每个设施安排的旅行的日期和时间。 因此,当我创建潜在客户并向潜在客户添加设施时,我有一个额外的字段,我可以在其中输入日期和时间,然后作为设施列表访问它,并在潜在客户页面上显示其游览日期。

我遇到了许多使用“通过”的字段,但我不确定在这种情况下这是否是正确的选择。 ManyToMany Fields with extra fields

我如何使用“通过”来使用多对多字段,并为我添加到多对多字段的潜在客户的每个设施自动生成​​通过字段?或者使用 through 不是一个好的选择?

我正在使用带有 React 前端的 Django Rest 框架:

模型.py

class Facility(models.Model):

    Name = models.CharField(max_length=150, null=True, blank=False)
    mainimage = models.ImageField(null=True, blank=True)
    Email = models.EmailField(max_length=150, null=True, blank=True)
    TelephoneNumber = models.CharField(max_length=30, null=True, blank=True)
    FacilityDescription = models.TextField(max_length=1000, null=True, blank=True)

    def __str__(self):
        return self.Name


class Lead(models.Model):
    assigned_facilities = models.ManyToManyField(Facility,  related_name='assigned_facilities', null=True, blank=True)

    first_name = models.CharField(max_length=40, null=True, blank=True)
    last_name = models.CharField(max_length=40, null=True, blank=True)


    def __str__(self):
        return f"{self.first_name} {self.last_name}"

序列化程序.py

class LeadUpdateSerializer(serializers.ModelSerializer):
    is_owner = serializers.SerializerMethodField()
    class Meta:
        model = Lead
        fields = (
            "id",
            "first_name",
            "last_name",
            "assigned_facilities",
        )
        read_only_fields = ("id")

    def get_is_owner(self, obj):
        user = self.context["request"].user
        return obj.agent == user

Leads.js

    const cpBoard = useSelector((state) => state.cpBoard);
    const facilityIds = (cpBoard.cpBoardItems?.map(cpBoardItem => (cpBoardItem.id)));

    function submitFacilities() {

        axios.patch(API.leads.update(id), { "assigned_facilities": facilityIds}, {

            headers: {
                "Authorization": `Bearer ${accessToken}`,
                'Accept' : 'application/json',
            },
            withCredentials: true,
        })
            .then(res => {
                fetchLeads()

            })
            .finally(() => {                
            })
    }

更新:

我目前正在尝试使用下面建议的解决方案,但是当我尝试更新潜在客户时出现错误:

AttributeError:“NoneType”对象没有属性“scheduled_datetime”文件“/serializers.py”,第 253 行,在 to_representation ret[“scheduled_datetime”] = str(instance.leadfacilityassociation.first().scheduled_datetime)

模型.py

class Facility(models.Model):

    name = models.CharField(max_length=150, null=True, blank=False)
    main_image = models.ImageField(null=True, blank=True)
    email = models.EmailField(max_length=150, null=True, blank=True)
    telephone_number = models.CharField(max_length=30, null=True, blank=True)
    facility_description = models.TextField(max_length=1000, null=True, blank=True)

    def __str__(self):
        return self.Name


class Lead(models.Model):
    first_name = models.CharField(max_length=40, null=True, blank=True)
    last_name = models.CharField(max_length=40, null=True, blank=True)

    def __str__(self):
        return f"{self.first_name} {self.last_name}"


class LeadFacilityAssociation(models.Model):
    assigned_facilities = models.ForeignKey(Facility,  related_name='leadfacilityassociation')
    lead = models.ForeignKey(Lead,  related_name='leadfacilityassociation')
    scheduled_datetime = models.DateTimeField(null=True, blank=True)

序列化程序.py

class LeadUpdateSerializer(serializers.ModelSerializer):
    is_owner = serializers.SerializerMethodField()
    assigned_facilities = serializers.Integer(required=True)
    scheduled_datetime = serializers.DateTimeField(required=True)

    class Meta:
        model = Lead
        fields = (
            "id",
            "first_name",
            "last_name",
            "assigned_facilities",
            "scheduled_datetime",
        )
        read_only_fields = ("id")

    def get_is_owner(self, obj):
        user = self.context["request"].user
        return obj.agent == user
    
    def create(self, validated_data):
        assigned_facilities = validated_data.pop("assigned_facilities")
        scheduled_datetime = validated_data.pop("scheduled_datetime")
        instance = Lead.objects.create(**validated_data)
        instance.leadfacilityassociation.create(assigned_facilities=assigned_facilities,scheduled_datetime=scheduled_datetime)
        return instance

    def to_representation(self, instance):
        ret = super().to_representation(instance)
        ret["scheduled_datetime"] = str(instance.leadfacilityassociation.first().scheduled_datetime)
        ret["assigned_facilities"] = instance.leadfacilityassociation.first().assigned_facilities
        return ret

【问题讨论】:

    标签: django django-rest-framework


    【解决方案1】:

    在您的情况下,使用 through= 似乎是可行的方法。要实现它,您需要为您的 M2M 表创建一个模型

    LeadFacility(models.Model):
        facility = models.ForeignKey(Facility, on_delete=models.CASCADE)
        lead = models.ForeignKey(Lead, on_delete=models.CASCADE)
        datetime = models.DateTimeField()
    

    您可以使用以下语法设置日期时间值:

    my_lead.assigned_facilities.add(my_facility, through_defautlts={"datetime": my_datetime})
    

    或者只是明确地创建 LeadFacilty :

    LeadFacility.objects.create(lead=my_lead, facility=my_facility, datetime=my_datetime)
    

    要访问这些字段,您将在 LeadFacility 模型中定义related_names

    【讨论】:

      【解决方案2】:

      ManyToManyField 基本上是在数据库级别创建另一个关联模型以将 ModelA(id) 与 ModelB(id) 关联。我们无法在其中添加其他字段。

      现在,在您的情况下,让我们重新构建架构。

      class Facility(models.Model):
      
          name = models.CharField(max_length=150, null=True, blank=False)
          main_image = models.ImageField(null=True, blank=True)
          email = models.EmailField(max_length=150, null=True, blank=True)
          telephone_number = models.CharField(max_length=30, null=True, blank=True)
          facility_description = models.TextField(max_length=1000, null=True, blank=True)
      
          def __str__(self):
              return self.Name
      
      
      class Lead(models.Model):
          first_name = models.CharField(max_length=40, null=True, blank=True)
          last_name = models.CharField(max_length=40, null=True, blank=True)
      
          def __str__(self):
              return f"{self.first_name} {self.last_name}"
      
      
      class LeadFacilityAssociation(models.Model):
          assigned_facilities = models.ForeignKey(Facility,  related_name='leadfacilityassociation')
          lead = models.ForeignKey(Lead,  related_name='leadfacilityassociation')
          scheduled_datetime = models.DateTimeField(null=True, blank=True)
      
      
      

      Class Facility:此Model 中的字段保持不变。

      Class Model:删除 assigned_facilities 字段,因为我们不再需要它。

      Class LeadFacilityAssociation:添加此Model 以将LeadFacility 与额外的scheduled_datetime 字段相关联。现在您可以将多个Leads 与多个Facility 与单独的scheduled_datetime 进行映射。

      串行器

      class LeadUpdateSerializer(serializers.ModelSerializer):
          is_owner = serializers.SerializerMethodField()
          assigned_facilities = serializers.Integer(required=True)
          scheduled_datetime = serializers.DateTimeField(required=True)
      
          class Meta:
              model = Lead
              fields = (
                  "id",
                  "first_name",
                  "last_name",
                  "assigned_facilities",
                  "scheduled_datetime",
              )
              read_only_fields = ("id")
      
          def get_is_owner(self, obj):
              user = self.context["request"].user
              return obj.agent == user
          
          def create(self, validated_data):
              assigned_facilities = validated_data.pop("assigned_facilities")
              scheduled_datetime = validated_data.pop("scheduled_datetime")
              instance = Lead.objects.create(**validated_data)
              instance.leadfacilityassociation.create(assigned_facilities=assigned_facilities,scheduled_datetime=scheduled_datetime)
              return instance
      
          def to_representation(self, instance):
              ret = super().to_representation(instance)
              ret["scheduled_datetime"] = str(instance.leadfacilityassociation.first().scheduled_datetime)
              ret["assigned_facilities"] = instance.leadfacilityassociation.first().assigned_facilities
              return ret
      

      笔记此序列化程序旨在仅创建一个Lead 和一个Facility。要创建多个设施,您可以更改覆盖的方法create。使用bulk_create 针对Lead 创建多个设施。还要更改 to_representation 方法体以针对 Lead 返回多个设施。

      【讨论】:

      • 当我尝试此操作时,当我尝试更新潜在客户时收到以下错误: AttributeError AttributeError: 'NoneType' object has no attribute 'scheduled_datetime' File "/serializers.py", line 253, in to_representation ret["scheduled_datetime"] = str(instance.leadfacilityassociation.first().scheduled_datetime)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-17
      • 1970-01-01
      • 1970-01-01
      • 2015-06-26
      • 1970-01-01
      • 2016-06-18
      • 1970-01-01
      相关资源
      最近更新 更多