Сохраните данные мастера форм Django в три модели со связанными полями

Я работаю над проектом, который требует использования мастера форм для заполнения трех связанных моделей. Первая модель - Listing - имеет общие данные, которые имеют отношение OneToOneField со второй моделью (Property). Модель 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)

Вот формы.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)

просмотры.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, но проблема заключается в том, чтобы получить ее первичный идентификатор и использовать его для сохранения модели Property. Опять же, модель ListingImages хранит изображения, связанные с моделью Listing. Как сохранить эти модели в базе данных, учитывая, что их несколько?


person japheth    schedule 26.06.2019    source источник
comment
Ошибка отступа? Последние три строки перед return в PropertyView выглядят так, как будто они должны быть частью цикла над form_list. Кроме того, вам нужно уточнить, что вы подразумеваете под первой моделью, третьей моделью и т. Д.   -  person nigel222    schedule 26.06.2019
comment
Отступ в порядке, потому что они не попадают под цикл for. Позвольте мне уточнить это название модели как можно скорее.   -  person japheth    schedule 26.06.2019
comment
И просто чтобы уточнить, что три строки работают хорошо, listing = listing_instance.save() сохраняет данные в базу данных, как и ожидалось. Проблема возникает со следующей строкой, поскольку первичный ключ не назначается в качестве внешнего ключа, как это должно быть.   -  person japheth    schedule 26.06.2019


Ответы (1)


Что не так, так это то, что описано здесь, 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()
person nigel222    schedule 26.06.2019
comment
Спасибо за этот ответ. Модель собственности теперь хорошо сохраняется. Однако для изображений я получаю сообщение об ошибке 'ListingImages' object is not iterable. Я использовал ваш код выше после настройки его в соответствии с моими определениями атрибутов. - person japheth; 26.06.2019
comment
Я удалил часть итерации, и она правильно сохраняет отдельный файл в базе данных, поэтому проблема заключается в обработке нескольких изображений. Как мне это сделать? - person japheth; 26.06.2019