【问题标题】:Save Django Form wizard data to three models with related fields将 Django 表单向导数据保存到三个具有相关字段的模型
【发布时间】:2019-11-08 11:07:12
【问题描述】:

我正在处理一个需要使用表单向导来填充三个相关模型的项目。第一个模型 - Listing - 具有与第二个模型 (Property) 具有 OneToOneField 关系的一般数据。 Listing 模型还与第三个模型(ListingImages) 具有多对多关系。一般来说,我在向导中使用了 4 个表单。这是模型定义 模型.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 模型相关的图像。考虑到它们是多个模型,如何将这些模型保存到数据库中?

【问题讨论】:

  • 缩进错误?在PropertyViewreturn 之前的最后三行看起来好像它们应该是form_list 循环的一部分。除此之外,您需要澄清“第一个模型”“第三个模型”等的含义。
  • 缩进是可以的,因为它们不属于 for 循环。让我尽快澄清模型命名。
  • 为了澄清三行运行良好,listing = listing_instance.save() 正在按预期将数据保存到数据库中。由于主键没有按照应有的方式分配为外键,因此后面的行出现了问题。

标签: django django-views django-formwizard django-file-upload


【解决方案1】:

问题是,如here 所述,model.save() 不返回保存的对象,而是返回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()

【讨论】:

  • 感谢您的回答。属性模型现在保存良好。但是,对于图像,我收到错误 'ListingImages' object is not iterable。在自定义代码以适合我的属性定义后,我使用了上面的代码。
  • 我删除了迭代部分,它正确地将单个文件保存在数据库中,所以问题在于处理多个图像。我该怎么做?
猜你喜欢
  • 1970-01-01
  • 2015-01-17
  • 2011-08-05
  • 1970-01-01
  • 2021-05-03
  • 1970-01-01
  • 2021-06-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多