Изменение изображения в Django inlineformset_factory ставит его в конец списка

Предположим, я создаю веб-приложение Django "Как", где пользователи пишут о том, как делать разные вещи, например.

  • "Как" сделать веревку
  • "Как" сделать глиняный горшок
  • "Как" научиться ездить на велосипеде

Вы поняли идею. Я сделал пост создания представления для этого. Теперь, когда участники делают пост. Они добавляют дополнительные изображения в пост

Пример: "Как" сделать веревку

  • Это имеет заголовок сообщения = Как сделать веревку
  • Post description = "Некоторое описание"
  • Опубликовать изображение = основное изображение

Теперь они должны показать изображения шаг за шагом, как сделана веревка

  • Изображение 1: Сделай это 1-й
  • Изображение 2: Сделайте это вторым

Я использую формы Django вместе с моей моделью поста, чтобы достичь этого. Все работает абсолютно нормально в представлении создания. Нет проблем. Но в обновленном представлении вещи ломаются.

Эта проблема

Проблема в том, что пользователь хочет отредактировать свой пост и переключить изображение № 2. со своего поста на другое изображение. Хотя они изменили 2-е изображение. Это изображение теперь заканчивается в самом конце списка. Заставить пользователя заново загрузить все изображения. Вернуть Орден. Делает мое приложение глючным.

Пример: предположим, что у пользователя есть пост ниже

main post Title 
" Some description "
Main Image = Post_image.jpg  

1st Image = A.jpg
   Image Title
   Image description
2nd Image = B.jpg
   Image Title
   Image description
3rd Image = C.jpg
   Image Title
   Image description
4st Image = D.jpg
    Image Title
     Image description
5th Image = E.jpg
     Image Title
     Image description
6th Image = F.img
     Image Title
     Image description

Теперь, если я изменил 2-е изображение B.jpg в b.jpg b.jpg перемещается в самый конец списка, и у вас есть порядок A, C, D, E, F, b

Ниже приведены мои модели:

 class Post(models.Model):
    user = models.ForeignKey(User, related_name='posts')
    created_at = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=250, unique=True)
    slug = models.SlugField(allow_unicode=True, unique=True,max_length=500)
    post_image = models.ImageField()
    message = models.TextField()

class Prep (models.Model): #(Images)
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='post_prep')
    image = models.ImageField(upload_to='images/', blank=True, null=True, default='')
    image_title = models.CharField(max_length=100, default='')
    image_description = models.CharField(max_length=250, default='')
    sequence = models.SmallIntegerField() ###########################ADDED THIS

    class Meta:    ###########################ADDED THIS
    unique_together = (('post', 'sequence'),) ###########################ADDED THIS
    ordering = ['sequence']  ###########################ADDED THIS

Мой пост создать вид

def post_create(request):
    ImageFormSet = modelformset_factory(Prep, fields=('image', 'image_title', 'image_description'), extra=12, max_num=12,
                                        min_num=2)
    if request.method == "POST":
        form = PostForm(request.POST or None, request.FILES or None)
        formset = ImageFormSet(request.POST or None, request.FILES or None)
        if form.is_valid() and formset.is_valid():
            instance = form.save(commit=False)
            instance.user = request.user
            instance.save()
            post_user = request.user
            for index, f in enumerate(formset.cleaned_data): #######CHANGED THIS
                try: ##############CHANGED THIS
                    photo = Prep(sequence=index, post=instance, image=f['image'], 
                             image_title=f['image_title'], image_description=f['image_description'])
                    photo.save()
                except Exception as e:
                    break

            return redirect('posts:single', username=instance.user.username, slug=instance.slug)
    else:
        form = PostForm()
        formset = ImageFormSet(queryset=Prep.objects.none())
    context = {
        'form': form,
        'formset': formset,
    }
    return render(request, 'posts/post_form.html', context)

Мой пост Edit View:

class PostPrepUpdate(LoginRequiredMixin, UpdateView):
    model = Post
    fields = ('title', 'message', 'post_image')
    template_name = 'posts/post_edit.html'
    success_url = reverse_lazy('home')

    def get_context_data(self, **kwargs):
        data = super(PostPrepUpdate, self).get_context_data(**kwargs)
        if self.request.POST:
            data['prep'] = PrepFormSet(self.request.POST, self.request.FILES, instance=self.object)
        else:
            data['prep'] = PrepFormSet(instance=self.object)
        return data

    def form_valid(self, form):
        context = self.get_context_data()
        prep = context['prep']
        with transaction.atomic():
            self.object = form.save()

            if prep.is_valid():
                prep.instance = self.object
                prep.save()
        return super(PostPrepUpdate, self).form_valid(form)

My Forms.py

class PostEditForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ('title', 'message', 'post_image', 'group', )


class PrepForm(forms.ModelForm): #####################CHANGED THIS
    class Meta:
        model = Prep
        fields = ('image', 'image_title', 'image_description', 'sequence')


PrepFormSet = inlineformset_factory(Post, Prep, form=PrepForm, extra=5, max_num=7, min_num=2)

*** Нужна помощь в устранении этой проблемы. Пример, если они меняют изображение 2. Затем он должен оставаться в позиции номер 2 и не двигаться в конец списка

1 ответ

Решение

В настоящее время вы не сохраняете порядок изображений, полагаясь на то, что они отображаются в том же порядке, в котором они были созданы. Добавление поля в Prep содержащее место изображения в последовательности изображений поможет:

class Prep (models.Model):
    # ...
    nr = SmallIntegerField()
    #...
    class Meta:
        unique_together = (('post', 'nr'),)

unique_together ограничение гарантирует, что каждый номер используется только один раз за пост. Это также позволяет изменять порядок изображений в сообщении без удаления и повторного создания всех Prep объекты.

При отображении поста, вам придется заказать Prep объекты по nr,


Что касается заполнения нового столбца, поскольку нет единого значения по умолчанию, которое имеет смысл, самый простой подход может быть следующим:

  • Добавить nr поле без unique_together ограничение и null=True первый; перенести изменения.
  • После миграции переберите существующий Prep объекты каждого Post в текущем порядке и присвойте им возрастающие номера.
  • После этого удалите null=True, добавлять unique_together и мигрировать снова.

unique_together нужны строковые параметры (он не может получить доступ к полям во внешнем классе); спасибо, что поймали это.


В форме редактирования вы хотите включить новое поле, чтобы пользователи могли поменять местами порядок двух изображений, поменяв местами их индексы. Вы просто должны предоставить им значимое сообщение об ошибке, если они используют дубликаты индексов.

Однако при создании пользователям не нужно явно указывать порядок, поскольку он подразумевается в последовательности изображений в наборе форм. Поэтому я бы предложил изменить цикл for следующим образом:

for index, f in enumerate(formset.cleaned_data):
    # ...
    photo = Prep(nr=index,
                 # add all other fields
                )

использование nr = index + 1 для удобных для человека индексов, начинающихся с 1. На самом деле, index или же image_index может быть лучшим названием для поля, чем nr,

Другие вопросы по тегам