25

私はこのような設定をしています(この質問のために簡略化されています):

class Employee(models.Model):
    name = models.CharField(name, unique=True)

class Project(models.Model):
    name = models.CharField(name, unique=True)
    employees = models.ManyToManyField(Employee)

従業員が削除されようとしているときに、彼がプロジェクトに接続されているかどうかを確認したいと思います。もしそうなら、削除は不可能なはずです。

信号とその操作方法について知っています。シグナルに接続して、のpre_deleteような例外をスローさせることができますValidationError。これにより削除が防止されますが、フォームなどによって適切に処理されません。

これは、他の人が遭遇する状況のようです。誰かがよりエレガントな解決策を指摘できることを願っています。

4

8 に答える 8

28

私はこの問題に対する答えを探していましたが、models.Model.delete() と QuerySet.delete() の両方で機能する適切な答えを見つけることができませんでした。私はそれに沿って進み、Steve K のソリューションを実装しました。このソリューションを使用して、オブジェクト (この例では Employee) をデータベースから削除することはできませんが、非アクティブに設定されていることを確認しました。

それは遅い答えです..他の人が見ているためだけに、私はここに私の解決策を入れています。

コードは次のとおりです。

class CustomQuerySet(QuerySet):
    def delete(self):
        self.update(active=False)


class ActiveManager(models.Manager):
    def active(self):
        return self.model.objects.filter(active=True)

    def get_queryset(self):
        return CustomQuerySet(self.model, using=self._db)


class Employee(models.Model):
    name = models.CharField(name, unique=True)
    active = models.BooleanField(default=True, editable=False)

    objects = ActiveManager()

    def delete(self):
        self.active = False
        self.save()

使用法:

Employee.objects.active() # use it just like you would .all()

または管理者で:

class Employee(admin.ModelAdmin):

    def queryset(self, request):
        return super(Employee, self).queryset(request).filter(active=True)
于 2013-09-25T14:25:31.013 に答える
9

これで、アプリの実装からソリューションが完成します。一部のコードは、LWN の回答からのものです。

データが削除される 4 つの状況があります。

  • SQL クエリ
  • delete()Model インスタンスの呼び出し:project.delete()
  • QuerySetdelete()インスタンスの呼び出し:Project.objects.all().delete()
  • 他モデルのForeignKeyフィールドで削除

最初のケースでできることはあまりありませんが、他の 3 つのケースはきめ細かく制御できます。アドバイスの 1 つは、ほとんどの場合、データ自体は決して削除しないでください。これらのデータは、アプリケーションの履歴と使用状況を反映しているためです。active代わりに、ブール フィールドでの設定が優先されます。

Model インスタンスを防ぐdelete()には、Model 宣言でサブクラス化delete()します。

    def delete(self):
        self.active = False
        self.save(update_fields=('active',))

QuerySetインスタンスでは、 LWN の回答のdelete()ように、カスタム オブジェクト マネージャーを使用して少しセットアップする必要があります。

これを再利用可能な実装にまとめます。

class ActiveQuerySet(models.QuerySet):
    def delete(self):
        self.save(update_fields=('active',))


class ActiveManager(models.Manager):
    def active(self):
        return self.model.objects.filter(active=True)

    def get_queryset(self):
        return ActiveQuerySet(self.model, using=self._db)


class ActiveModel(models.Model):
    """ Use `active` state of model instead of delete it
    """
    active = models.BooleanField(default=True, editable=False)
    class Meta:
        abstract = True

    def delete(self):
        self.active = False
        self.save()

    objects = ActiveManager()

使用法、クラスをサブクラス化するだけActiveModelです:

class Project(ActiveModel):
    ...

それでも、ForeignKey フィールドのいずれかが削除された場合でも、オブジェクトを削除できます。

class Employee(models.Model):
    name = models.CharField(name, unique=True)

class Project(models.Model):
    name = models.CharField(name, unique=True)
    manager = purchaser = models.ForeignKey(
        Employee, related_name='project_as_manager')

>>> manager.delete() # this would cause `project` deleted as well

これは、Model フィールドのon_delete 引数を追加することで防ぐことができます。

class Project(models.Model):
    name = models.CharField(name, unique=True)
    manager = purchaser = models.ForeignKey(
        Employee, related_name='project_as_manager',
        on_delete=models.PROTECT)

のデフォルトはon_delete、(のサブクラス)を発生させる代わりにCASCADE使用することにより、インスタンスが削除されます。これのもう 1 つの目的は、データの ForeignKey を参照として保持することです。PROTECTProtectedErrorIntegrityError

于 2016-07-07T10:58:45.113 に答える
5

大量の従業員の削除の試行が決してないことがわかっている場合は、モデルをオーバーライドして、それが合法的な操作である場合にdeleteのみ呼び出すことができます。super

残念ながら、呼び出す可能性のあるものはすべてqueryset.delete()SQL に直接送られます: http://docs.djangoproject.com/en/dev/topics/db/queries/#deleting-objects

queryset.delete()しかし、このコードを書いているのはあなたであり、従業員がまったくいないことを確認できるため、それほど問題はないと思います。手動で呼び出しdelete()ます。

従業員の削除が比較的まれであることを願っています。

def delete(self, *args, **kwargs):
    if not self.related_query.all():
        super(MyModel, self).delete(*args, **kwargs)
于 2011-01-28T07:48:13.033 に答える
2

提案がありますが、それがあなたの現在のアイデアよりも優れているかどうかはわかりません。ここで、遠いが無関係ではない問題の答えを見てください。基本的にそれらを削除して独自のものを使用することにより、django管理者のさまざまなアクションをオーバーライドできます。たとえば、次のような場所があります。

def really_delete_selected(self, request, queryset):
    deleted = 0
    notdeleted = 0
    for obj in queryset:
        if obj.project_set.all().count() > 0:
            # set status to fail
            notdeleted = notdeleted + 1
            pass
        else:
            obj.delete()
            deleted = deleted + 1
    # ...

私のようにdjango管理者を使用していない場合は、ユーザーがオブジェクトを削除できるようにする前に、そのチェックをUIロジックに組み込むだけです。

于 2011-01-28T07:44:51.340 に答える