0

このオブジェクトのインスタンスを作成するときに、別のモデルのインスタンス (B と呼びます) を content_object フィールドの初期化子として渡します (コンストラクターの kwargs を介して)。

A を作成する前に B を保存しないと、A を保存するときに content_object_id が NULL としてデータベースに保存されます。A のコンストラクターに渡す前にB を保存すると、すべて問題ありません。

それは論理的ではありません。A.save() を実行するときに関連オブジェクト (B) の ID がフェッチされ、B がまだ保存されていない場合は何らかの例外をスローする必要があると想定しましたが、黙って失敗するだけです。オブジェクトを廃棄するだけでなく、常にオブジェクトを保持する意思があるかどうかまだわからないため、現在のソリューション (事前に B を保存) は好きではありません。また、パフォーマンスに関する考慮事項もあります。別のデータを追加するとどうなるでしょうか。すぐにもう一度保存します。

class BaseNodeData(models.Model):
    ...
    extnodedata_content_type = models.ForeignKey(ContentType, null=True)
    extnodedata_object_id = models.PositiveIntegerField(null=True)
    extnodedata = generic.GenericForeignKey(ct_field='extnodedata_content_type', fk_field='extnodedata_object_id')

class MarkupNodeData(models.Model):
    raw_content = models.TextField()

次のようにするとします。

markup = MarkupNodeData(raw_content='...')
base = BaseNodeData(..., extnodedata=markup)
markup.save()
base.save()
# both records are inserted to the DB but base is stored with extnodedata_object_id=NULL

markup = MarkupNodeData(raw_content='...')
base = BaseNodeData(..., extnodedata=markup)
base.save()
markup.save()
# no exception is thrown and everything is the same as above

markup = MarkupNodeData(raw_content='...')
markup.save()
base = BaseNodeData(..., extnodedata=markup)
base.save()
# this works as expected

もちろん、私はこの方法でそれを行うことができますが、何も変わりません:

base = BaseNodeData(...)
base.extnodedata = markup

私の質問は、これは私が報告すべきジャンゴのバグですか、それとも何か間違ったことをしているのかもしれません。GenericRelations のドキュメントは厳密には冗長ではありません。

4

3 に答える 3

0

事前にBを保存せずにAを保存できるのはおかしいと思います。

しかし、残したくないオブジェクトとの関係を設定することは考えられません。存在しないオブジェクトとの関係を持つことは意味がありません;-)したがって、事前にBを保存することは私にとっては問題ありません。

これが役立つかどうかはわかりませんが、この質問に投稿されているように、createメソッドを使用すると、一般的なリレーションで何ができるかがわかる場合があります。

于 2011-01-02T23:48:56.393 に答える
0

B のインスタンスは、保存前の pk が None であり、extnodedata_object_id フィールドで null 値が許可されているため、A インスタンスの保存は有効です。

Aの保存をオーバーライドして、Bの新しいインスタンスを適切に処理することがうまくいくようです。例 (未テスト):

def save(self, *args, **kwargs):
    b = self.extnodedata
    if b and b.pk is None:
        b.save()
        self.extnodedata = b
    return super(BaseNodeData, self).save(*args, **kwargs)
于 2011-01-03T16:39:24.197 に答える
0

回答ありがとうございます。私はもう少し時間をかけて django のソースを調査することにし、自分で解決策を考え出しました。GenericForeignKey をサブクラス化しました。コードは自明である必要があります。

from django.contrib.contenttypes import generic
from django.db.models import signals

class ImprovedGenericForeignKey(generic.GenericForeignKey):
    """
    Corrects the behaviour of GenericForeignKey so even if you firstly
    assign an object to this field and save it after its PK gets saved.

    If you assign a not yet saved object to this field an exception is 
    thrown upon saving the model.
    """

    class IncompleteData(Exception):
        message = 'Object assigned to field "%s" doesn\'t have a PK (save it first)!'

        def __init__(self, field_name):
            self.field_name = field_name

        def __str__(self):
            return self.message % self.field_name

    def contribute_to_class(self, cls, name):
        signals.pre_save.connect(self.instance_pre_save, sender=cls, weak=False)
        super(ImprovedGenericForeignKey, self).contribute_to_class(cls, name)

    def instance_pre_save(self, sender, instance, **kwargs):
        """
        Ensures that if GenericForeignKey has an object assigned
        that the fk_field stores the object's PK.
        """

        """ If we already have pk set don't do anything... """
        if getattr(instance, self.fk_field) is not None: return

        value = getattr(instance, self.name)

        """
        If no objects is assigned then we leave it as it is. If null constraints
        are present they should take care of this, if not, well, it's not my fault;)
        """
        if value is not None:
            fk = value._get_pk_val()

            if fk is None:
                raise self.IncompleteData(self.name)

            setattr(instance, self.fk_field, fk)

これはdjangoのバグと考えるべきだと思いますので、報告して様子を見てみます。

于 2011-01-03T20:01:27.657 に答える