Как в Django views обработать несколько formset?
Делаю сайт конструктор, который состовляется из блоков. У каждого блока есть своя сущность, котороые могут иметь еще дополнительный связанные сущности. Например: есть сущность странца, у неё полиморфная связь с разными типа блоков ( галлерея, услуги ..). У Галлереи может быть несколько картинок, у блока услуг может быть несколько услуг.
Делаю страницу редактирования контента в виде большой формы состоящей из "джанговских" форм и формсетов. Проблема в том, что у форм и формсетов, которые я формирую во viewa.py аттрибуты input'ов не уникальный, поэтому когда в request.POST приходят поля со списками значений.
request.POST = <QueryDict: {'csrfmiddlewaretoken': ['QqdKa3H3J2cgCs1tH7iRWuLtmzSjHeSOZI3gXes2Dc6zBfjn0HzBU20mBmuwjdkz', 'QqdKa3H3J2cgCs1tH7iRWuLtmzSjHeSOZI3gXes2Dc6zBfjn0HzBU20mBmuwjdkz'], 'title': ['Контакты123', 'Контакты123', 'Мои работы', 'Контакты'], 'string_1': ['Щербина'], 'string_2': ['Владимир'], 'string_3': ['123'], 'subtitle': ['фотографирую сам'], 'background': [''], 'form-TOTAL_FORMS': ['2', '4'], 'form-INITIAL_FORMS': ['1', '3'], 'form-MIN_NUM_FORMS': ['0', '0'], 'form-MAX_NUM_FORMS': ['1000', '1000'], 'form-0-picture': ['', ''], 'form-0-title': ['Услуга номер 123'], 'form-0-description': ['Описание', 'Описание'], 'form-0-id': ['34', '34'], 'form-1-picture': ['', ''], 'form-1-title': [''], 'form-1-description': ['', 'Описание'], 'form-1-id': ['', '35'], 'form-2-picture': [''], 'form-2-description': ['Описание'], 'form-2-id': ['36'], 'form-3-picture': [''], 'form-3-description': [''], 'form-3-id': [''], 'picture': [''], 'form-0-type': ['4'], 'form-0-value': ['89882530660'], 'form-0-show_in_header': ['on'], 'form-1-type': ['7'], 'form-1-value': ['[email protected]'], 'form-1-show_in_header': ['on'], 'form-2-type': ['4'], 'form-2-value': ['89882530660'], 'form-2-show_in_header': ['on'], 'form-3-type': ['7'], 'form-3-value': ['[email protected]'], 'form-3-show_in_header': ['on'], 'form-4-type': [''], 'form-4-value': [''], 'form-4-show_in_header': ['0']}>
как в formset изменить префикс для аттрибута 'name'? Например вместо 'form-0-title' было 'gallery-0-title'
Модели:
class Landing(models.Model):
account = models.ForeignKey(Account, on_delete=models.CASCADE)
services = models.ManyToManyField(Service)
landing_slug = models.CharField(max_length=100, verbose_name="Адрес страницы", unique=True)
title = models.CharField(max_length=80, default="", verbose_name="Заголовок")
footer_text = models.CharField(max_length=180, default="", verbose_name="Текст футера", blank=True)
publish = models.BooleanField(verbose_name="Опубликовать страницу (сайт)", default=0)
def add_block(self, block) -> 'Block':
block_type = ContentType.objects.get_for_model(block)
return Block.objects.create(
landing = self,
block_type = block_type,
block_id = block.id,
)
def __str__(self):
return self.landing_slug
class Meta:
verbose_name = 'Страница'
verbose_name_plural = 'Страницы'
class Block(models.Model):
landing = models.ForeignKey(Landing, on_delete=models.CASCADE, related_name='block_landing')
limit =models.Q(app_label='landing', model='galleryblock') | models.Q(app_label='landing', model='serviceblock')| models.Q(app_label='landing', model='mainblock')| models.Q(app_label='landing', model='contactsblock')
block_id = models.PositiveIntegerField(null=True)
block_type = models.ForeignKey(ContentType, on_delete=models.PROTECT, null=True, limit_choices_to=limit,)
block = GenericForeignKey('block_type', 'block_id')
index = models.PositiveIntegerField(default=0, blank=False, null=False)
class Meta:
ordering = ['index']
def __str__(self):
return str(self.block_type.name)
class GalleryBlock(models.Model):
block = GenericRelation(Block, related_name= 'gallery_block', related_query_name= 'галлерея')
title =models.CharField(max_length=80, default="", verbose_name="Заголовок галлереи")
template_id = models.CharField(max_length=80, default='', verbose_name='Аттрибут id для html')
def __str__(self):
return self.title
class Meta:
verbose_name = 'Галлерея'
verbose_name_plural = 'Галлерея'
class GalleryBlockItem(models.Model):
block = models.ForeignKey(GalleryBlock, on_delete=models.CASCADE)
picture = models.ImageField(verbose_name='Картинка', upload_to='img')
description = models.TextField(default="", verbose_name="Описание")
class ServiceBlock(models.Model):
block = GenericRelation(Block, related_name= 'service_block', related_query_name= 'услуги')
title =models.CharField(max_length=80, default="", verbose_name="Заголовок блока услуг")
template_id = models.CharField(max_length=80, default='', verbose_name='Аттрибут id для html')
def __str__(self):
return self.title
class Meta:
verbose_name = 'Блок услуг'
verbose_name_plural = 'Блок услуг'
class ServiceBlockItem(models.Model):
block = models.ForeignKey(ServiceBlock, on_delete=models.CASCADE)
picture = models.ImageField(verbose_name='Картинка', upload_to='img/icons')
title = models.CharField(max_length=80,default="", verbose_name="Название услуги")
description = models.TextField(default="", verbose_name="Описание услуги")
class Meta:
verbose_name = 'Услуга'
verbose_name_plural = 'Услуги'
def __str__(self):
return self.title
views.py:
def edit(request,landing_slug=None):
context = {}
if request.user.is_authenticated:
pass
else: return redirect ('login_page')
if landing_slug == None:
land = Landing.objects.filter(account=Account.objects.get(user=request.user))[0]
return redirect('/edit/' + land.landing_slug)
else:
land = Landing.objects.get(landing_slug=landing_slug)
ContactsLandingFormset=modelformset_factory(ContactsLanding,exclude=['landing'], can_delete=True)
blocks = Block.objects.filter(landing=land)
contacts = ContactsLanding.objects.filter(landing=land)
print(request.POST)
if request.method == 'GET':
land_form = LandingForm(instance=land)
contacts_formset = ContactsLandingFormset(queryset=contacts,auto_id="contact-id-%s")
context['contacts_formset'] = contacts_formset
context['land_form'] = land_form
context = add_context_for_landing(context, land)
for block in blocks:
if block.block_type.name == 'Главный блок':
context['main_block_form']= MainBlockForm(instance=block.block)
if block.block_type.name == 'Блок услуг':
ServicesFormset = modelformset_factory(ServiceBlockItem, exclude=['block'], can_delete=True,)
context['services_block_form'] = ServicesBlockForm(instance=block.block)
context['services_block_items_formset']= ServicesFormset(queryset=ServiceBlockItem.objects.filter(block=block.block), auto_id="service-id-%s")
if block.block_type.name == 'Галлерея':
GalleryFormset = modelformset_factory(GalleryBlockItem, exclude=['block'], can_delete=True)
context['gallery_block_form'] = GalleryBlockForm(instance=block.block)
context['gallery_block_items_formset']= GalleryFormset(queryset=GalleryBlockItem.objects.filter(block=block.block), auto_id="gallery-id-%s")
if block.block_type.name == 'Блок котнактов':
context['contacts_block_form']= ContactsBlockForm(instance=block.block)
return render(request, 'edit.html', context)
elif request.method == 'POST':
land_form = LandingForm(request.POST, instance=land)
contacts_formset = ContactsLandingFormset(request.POST, queryset=contacts, auto_id="contact-id-%s")
for block in blocks:
if block.block_type.name == 'Главный блок':
main_block_form = MainBlockForm(request.POST, request.FILES, instance=block.block)
if block.block_type.name == 'Блок услуг':
ServicesFormset = modelformset_factory(ServiceBlockItem, exclude=['block'], can_delete=True)
services_block_form = ServicesBlockForm(request.POST, instance=block.block)
services_block_items_formset = ServicesFormset(request.POST, queryset=ServiceBlockItem.objects.filter(
block=block.block), auto_id="service-id-%s")
try:
if services_block_form.is_valid():
services_block_form.save()
if services_block_items_formset.is_valid():
for services_block_item_form in services_block_items_formset:
if services_block_item_form.cleaned_data.get('id'):
print('Save')
serv_item = services_block_item_form.save(commit=False)
serv_item.block = services_block_form.instance
serv_item.save()
if block.block_type.name == 'Галлерея':
GalleryFormset = modelformset_factory(GalleryBlockItem, exclude=['block'], can_delete=True)
gallery_block_form = GalleryBlockForm(request.POST, instance=block.block)
gallery_block_items_formset = GalleryFormset(request.POST,
queryset=GalleryBlockItem.objects.filter(
block=block.block), auto_id="gallery-id-%s")
if block.block_type.name == 'Блок котнактов':
context['contacts_block_form'] = ContactsBlockForm(request.POST, instance=block.block)
return redirect('/edit')
шаблон(сокращенный вариант):
<form method="post" action="" enctype='multipart/form-data'>{% csrf_token %}
{{land_form }}
{{contacts_formset}}
{{main_block_form}}
{{services_block_form}}
{{services_block_items_formset}}
{{gallery_block_form}}
{{gallery_block_items_formset}}
<button type="submit" >Сохранить</button>
</form>