将 Django Form 向导数据保存到三个具有相关字段的模型

Save Django Form wizard data to three models with related fields

我正在从事一个需要使用表单向导来填充三个相关模型的项目。第一个模型 - Listing - 具有与第二个模型 (Property)OneToOneField 关系的一般数据。 Listing 模型也与第三个模型 (ListingImages) 存在多对多关系。通常,我在向导中使用 4 个表单。这是模型定义 models.py

class Listing(models.Model):
    listing_type_choices = [('P', 'Property'), ('V', 'Vehicle'), ('B', 'Business/Service'), ('E', 'Events')]

    listing_title = models.CharField(max_length=255)
    listing_type = models.CharField(choices=listing_type_choices, max_length=1, default='P')
    status = models.BooleanField(default=False)
    featured = models.BooleanField(default=False)
    city = models.CharField(max_length=255, blank=True)
    location = PlainLocationField(based_fields=['city'], zoom=7, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    expires_on = models.DateTimeField(auto_now=True)
    created_by = models.ForeignKey(User,
        on_delete=models.CASCADE, editable=False, null=True, blank=True
    )
    listing_owner = models.ForeignKey(User,
        on_delete=models.CASCADE, related_name='list_owner'
    )

    def __str__(self):
        return self.listing_title


def get_image_filename(instance, filename):
    title = instance.listing.listing_title
    slug = slugify(title)
    return "listings_pics/%s-%s" % (slug, filename)


class ListingImages(models.Model):
    listing = models.ForeignKey(Listing, on_delete=models.CASCADE)
    image_url = models.ImageField(upload_to=get_image_filename,
                              verbose_name='Listing Images')
    main_image = models.BooleanField(default=False)

    class Meta:
        verbose_name_plural = "Listing Images"

    def __str__(self):
        return f'{self.listing.listing_title} Image'


class Property(models.Model):
    sale_hire_choices = [('S', 'Sale'), ('R', 'Rent')]
    fully_furnished_choices = [('Y', 'Yes'), ('N', 'No')]

    listing = models.OneToOneField(Listing, on_delete=models.CASCADE)
    sub_category = models.ForeignKey(PropertySubCategory, on_delete=models.CASCADE)
    for_sale_rent = models.CharField(choices=sale_hire_choices, max_length=1, default=None)
    bedrooms = models.PositiveIntegerField(default=0)
    bathrooms = models.PositiveIntegerField(default=0)
    rooms = models.PositiveIntegerField(default=0)
    land_size = models.DecimalField(max_digits=10, decimal_places=2)
    available_from = models.DateField()
    car_spaces = models.PositiveIntegerField(default=0)
    fully_furnished = models.CharField(choices=fully_furnished_choices, max_length=1, default=None)
    desc = models.TextField()
    property_features = models.ManyToManyField(PropertyFeatures)
    price = models.DecimalField(max_digits=15, decimal_places=2)
    currency = models.ForeignKey(Currency, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

这里是forms.py

    from django import forms
from .models import Listing, Property, Vehicle, Business, ListingImages
from django.forms import modelformset_factory

class ListingDetails(forms.ModelForm):
    class Meta:
        model = Listing
        fields = ['listing_title', 'city', 'location']

class PropertyDetails1(forms.ModelForm):
    class Meta:
        model = Property
        fields = ['sub_category', 'for_sale_rent', 'bedrooms', 'bathrooms',
            'rooms', 'land_size', 'available_from', 'car_spaces', 'fully_furnished',
            'desc', 'currency', 'price'
        ]

class PropertyDetails2(forms.ModelForm):
    class Meta:
        model = Property
        fields = ['property_features']

class ListingImagesForm(forms.ModelForm):
    image_url = forms.ImageField(label='Listing Image',
        widget=forms.ClearableFileInput(attrs={'multiple': True}),
        required=False
    )
    class Meta:
        model = ListingImages
        fields = ['image_url']

ImageFormSet = modelformset_factory(ListingImages, form=ListingImagesForm, extra=3)

views.py

    from django.shortcuts import render, redirect
import os
from .forms import ListingDetails, PropertyDetails1, PropertyDetails2, ListingImagesForm
from .models import ListingImages, Listing, Property
from formtools.wizard.views import SessionWizardView
from django.conf import settings
from django.core.files.storage import FileSystemStorage
from django.forms import modelformset_factory
from django.contrib import messages
from django.http import HttpResponseRedirect, HttpResponse
from django.forms.models import construct_instance

class PropertyView(SessionWizardView):
    # formset = ImageFormSet(queryset=Images.objects.none())
    template_name = "listings/create_property.html"
    form_list = [ListingDetails, PropertyDetails1, PropertyDetails2, ListingImagesForm]
    file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'media'))
    def done(self, form_list, **kwargs):
        listing_instance = Listing()
        property_instance = Property()
        listing_instance.created_by = self.request.user
        listing_instance.listing_owner = self.request.user
        listing_instance.listing_type = 'P'
        for form in form_list:
            listing_instance = construct_instance(form, listing_instance, form._meta.fields, form._meta.exclude)
            property_instance = construct_instance(form, property_instance, form._meta.fields, form._meta.exclude)
        listing = listing_instance.save()
        property_instance.listing = listing
        property_instance.save()
        return HttpResponse('data saved successfully')

我面临的问题是我能够保存 Listing 模型,但问题是获取其主 ID 并使用它来保存 Property 模型。同样,ListingImages 模型存储与 Listing 模型相关的图像。考虑到它们是多个,我如何将这些模型保存到数据库?

问题在于,如 here 所述,model.save() 不是 return 保存的对象,而是 None.

所以上面代码的最后几行应该是

    listing_instance.save()
    property_instance.listing = listing_instance
    property_instance.save()
    return HttpResponse('data saved successfully')

同上保存一组 listing_images 就像

    for li_obj in listing_image_instances:
         li_obj.listing = listing_instance # saved above
         li_obj.save()