使用Django在数据库中交换2个对象的表单

时间:2012-01-19 14:41:46

标签: python django

我是django的新手,过去一个月左右我一直在开发一个简单的应用程序,但我遇到了一个问题,我无法做到这一点。 我有一个名为WeeklyPlaylist的简单模型(来自我的models.py):

class WeeklyPlaylist(models.Model):
    total_num_entries_in_playlist = 8
    get_pos_choices = get_integer_choices(1, total_num_entries_in_playlist)
    sched = models.ForeignKey(Schedule)
    week = models.IntegerField(choices=get_integer_choices(1, 52))
    position = models.IntegerField(choices=get_pos_choices)

其中'position'仅表示视频在播放列表中的位置。 我想通过上面模型的更改/更新表单(来自我的admin.py),让管理员能够将播放列表中一个视频的位置与同一个播放列表中的另一个视频进行交换:

class WeeklyPlaylistAdmin(admin.ModelAdmin):
    (...)
    readonly_fields = ('position',)
    form = WeeklyPlaylistAdminForm

    def get_changelist_form(self, request, obj=None, **kwargs):
        return WeeklyPlaylistAdminForm

我正在为这个对象定义我自己的表单(仍然来自admin.py):

class WeeklyPlaylistAdminForm(ModelForm):
    class Meta:
        model = WeeklyPlaylist
        fields = ('position',)

    swap_with_position = forms.IntegerField(widget=forms.Select(choices=WeeklyPlaylist.get_pos_choices))

    def clean_swap_with_position(self):
        swap_with_position = self.cleaned_data['swap_with_position']
        instance = getattr(self, 'instance', None)
        if instance and instance.id and swap_with_position == self.instance.position:
            raise forms.ValidationError("You must specify a different position than the actual one.")

        # select the database obj to swap position with
        other = WeeklyPlaylist.objects.filter(sched__screen__name=self.instance.sched.screen.name, sched__year__exact=self.instance.sched.year, week=self.instance.week, position=swap_with_position)
        if other.count() != 1:
            raise forms.ValidationError("The desired position does not correspond to any existing WeeklyPlaylist entry.")

        return swap_with_position

我的想法主要是在WeeklyPlaylist模型的更改/更新表单中为管理员提供额外的“选择”html标签,他可以在播放列表中输入当前视频的新位置,必须检查相关的clean_方法,以确保所需的播放列表位置有效。 到现在为止还挺好。现在,我的问题如下:当管理员点击“保存”按钮时,如何同时保存修改后的对象,以及与其交换位置的对象?我尝试使用以下代码在表单的save()方法中执行此操作:

def save(self, commit=True, *args, **kwargs):
    m = super(WeeklyPlaylistAdminForm, self).save(commit=False, *args, **kwargs)
    cleaned_data = self.cleaned_data
    swap_with_position = cleaned_data.get("swap_with_position")
    if commit:
        # select the database obj to swap position with
        other = WeeklyPlaylist.objects.get(sched__screen__name=m.sched.screen.name, sched__year__exact=m.sched.year, week=m.week, position=swap_with_position)
        m.position, other.position = other.position, m.position
        m.save()
        other.save()
    return m

这似乎完全没问题,除了由于某种原因,提交总是假的,即使在操作完成后保存“m”,这是我不明白的。但结果是,从不调用other.save(),如果我删除if语句检查commit的值,我将无法对WeeklyPlaylistAdminForm对象执行save(commit = False),这可以是恼人的... 那么,有什么建议可以帮到我吗? 提前谢谢了! 干杯! 阿德里安

1 个答案:

答案 0 :(得分:1)

当我在Django中做了类似的事情时,我还没有实现与另一个类别交换的模型,但他们重新排序列表;因此,使用5阶到0阶的移动元素会将所有先前元素的顺序从0到4向上移动,但是使用特定有序元素进行交换应该更容易。

我会在进行任何修改之前存储模型的先前位置,并在实际执行保存之前检测保存方法中是否已更改。如果它已经改变,那么我将保存当前模型,然后查找具有该位置且没有当前位置的模型,并对该位置进行更新以更正位置;希望以下代码可以帮助证明我的意思:

class WeeklyPlaylist(models.Model):
  def __init__(self, *args, **kwargs):
    super(WeeklyPlaylist, self).__init__(*args, **kwargs)
    self._position = int(self.position)

  def save(self, *args, **kwargs):
    super(WeeklyPlaylist, self).save(*args, **kwargs)

    # position has changed, so change the position of the element that held the new position now held by this element
    if int(self.position) != self._position:
      WeeklyPlaylist.objects.exclude(pk=self.pk).filter(
        position=self.position
      ).update(position=self._position)