115

私はこれをしたいと思っています:

class Place(models.Model):
   name = models.CharField(max_length=20)
   rating = models.DecimalField()

class LongNamedRestaurant(Place):  # Subclassing `Place`.
   name = models.CharField(max_length=255)  # Notice, I'm overriding `Place.name` to give it a longer length.
   food_type = models.CharField(max_length=25)

これは私が使用したいバージョンです(私はどんな提案も受け付けていますが): http ://docs.djangoproject.com/en/dev/topics/db/models/#id7

これはDjangoでサポートされていますか?そうでない場合、同様の結果を達成する方法はありますか?

4

10 に答える 10

76

更新された回答:コメントで指摘されているように、元の回答は質問に適切に回答していませんでした。実際、LongNamedRestaurantデータベースで作成されたのはモデルだけで、そうでPlaceはありませんでした。

解決策は、「場所」を表す抽象的なモデルを作成することです。AbstractPlace、そしてそれから継承します:

class AbstractPlace(models.Model):
    name = models.CharField(max_length=20)
    rating = models.DecimalField()

    class Meta:
        abstract = True

class Place(AbstractPlace):
    pass

class LongNamedRestaurant(AbstractPlace):
    name = models.CharField(max_length=255)
    food_type = models.CharField(max_length=25)

@Markの回答も読んでください。彼は、非抽象クラスから継承された属性を変更できない理由について優れた説明をしています。

(これは、Django 1.10以降でのみ可能であることに注意してください。Django1.10より前では、抽象クラスから継承された属性を変更することはできませんでした。)

元の回答

Django 1.10以降、それは可能です!あなたはただあなたが求めたことをしなければなりません:

class Place(models.Model):
    name = models.CharField(max_length=20)
    rating = models.DecimalField()

    class Meta:
        abstract = True

class LongNamedRestaurant(Place):  # Subclassing `Place`.
    name = models.CharField(max_length=255)  # Notice, I'm overriding `Place.name` to give it a longer length.
    food_type = models.CharField(max_length=25)
于 2016-09-01T18:26:56.980 に答える
62

いいえ、そうではありません

フィールド名「非表示」は許可されていません

通常のPythonクラスの継承では、子クラスが親クラスの属性をオーバーライドすることが許可されています。Djangoでは、これはFieldインスタンスである属性には許可されていません(少なくとも現時点では許可されていません)。基本クラスにと呼ばれるフィールドがある場合、その基本クラスから継承するクラスでauthor呼び出される別のモデルフィールドを作成することはできません。author

于 2010-03-01T18:08:20.913 に答える
30

これは抽象的でない限り不可能であり、その理由は次のとおりLongNamedRestaurantです。はPlace、クラスとしてだけでなく、データベース内でもあります。Placeplace-tableには、すべての純粋およびすべてののエントリが含まれていLongNamedRestaurantます。とplaceテーブルへの参照をLongNamedRestaurant使用して追加のテーブルを作成するだけです。food_type

を実行するとPlace.objects.all()、であるすべての場所も取得され、 (なしで)LongNamedRestaurantのインスタンスになります。したがって、同じデータベース列を共有するため、同じタイプである必要があります。Placefood_typePlace.nameLongNamedRestaurant.name

これは通常のモデルには理にかなっていると思います。すべてのレストランは場所であり、少なくともその場所にあるすべてのものが必要です。おそらく、この一貫性は、1.10より前の抽象モデルではデータベースの問題を引き起こさなかったものの、それが不可能だった理由でもあります。@lampslaveの発言として、1.10で可能になりました。個人的に注意をお勧めします。Sub.xがSuper.xをオーバーライドする場合は、Sub.xがSuper.xのサブクラスであることを確認してください。そうでない場合、Superの代わりにSubを使用することはできません。

回避策AUTH_USER_MODEL:電子メールフィールドを変更するだけでよい場合は、かなりのコード重複を伴うカスタムユーザーモデル()を作成できます。または、メールをそのままにして、すべてのフォームで必要であることを確認することもできます。これは、他のアプリケーションがデータベースを使用している場合、データベースの整合性を保証するものではなく、逆の方法でも機能しません(ユーザー名を不要にしたい場合)。

于 2013-08-10T07:13:11.633 に答える
19

https://stackoverflow.com/a/6379556/15690を参照してください:

class BaseMessage(models.Model):
    is_public = models.BooleanField(default=False)
    # some more fields...

    class Meta:
        abstract = True

class Message(BaseMessage):
    # some fields...
Message._meta.get_field('is_public').default = True
于 2014-07-04T23:04:06.700 に答える
9

コードを新しいアプリに貼り付け、INSTALLED_APPSにアプリを追加して、syncdbを実行しました。

django.core.exceptions.FieldError: Local field 'name' in class 'LongNamedRestaurant' clashes with field of similar name from base class 'Place'

Djangoはそれをサポートしていないようです。

于 2010-02-26T21:43:43.310 に答える
8

私の解決策は次のように単純です。モデルのフィールドの属性をmonkey patchingどのように変更したかに注目してください。max_lengthnameLongNamedRestaurant

class Place(models.Model):
   name = models.CharField(max_length=20)

class LongNamedRestaurant(Place):
    food_type = models.CharField(max_length=25)
    Place._meta.get_field('name').max_length = 255
于 2019-07-17T07:10:21.990 に答える
7

この非常に優れたコードにより、抽象親クラスのフィールドを「オーバーライド」できます。

def AbstractClassWithoutFieldsNamed(cls, *excl):
    """
    Removes unwanted fields from abstract base classes.

    Usage::
    >>> from oscar.apps.address.abstract_models import AbstractBillingAddress

    >>> from koe.meta import AbstractClassWithoutFieldsNamed as without
    >>> class BillingAddress(without(AbstractBillingAddress, 'phone_number')):
    ...     pass
    """
    if cls._meta.abstract:
        remove_fields = [f for f in cls._meta.local_fields if f.name in excl]
        for f in remove_fields:
            cls._meta.local_fields.remove(f)
        return cls
    else:
        raise Exception("Not an abstract model")

フィールドが抽象親クラスから削除されたら、必要に応じて自由に再定義できます。

これは私自身の仕事ではありません。ここからの元のコード:https ://gist.github.com/specialunderwear/9d917ddacf3547b646ba

于 2015-09-22T15:11:47.807 に答える
6

多分あなたはcontribute_to_classを扱うことができます:

class LongNamedRestaurant(Place):

    food_type = models.CharField(max_length=25)

    def __init__(self, *args, **kwargs):
        super(LongNamedRestaurant, self).__init__(*args, **kwargs)
        name = models.CharField(max_length=255)
        name.contribute_to_class(self, 'name')

Syncdbは正常に動作します。私はこの例を試しませんでした。私の場合、制約パラメータをオーバーライドするだけなので...待って見てください!

于 2010-06-23T05:18:36.177 に答える
4

私はそれが古い質問であることを知っていますが、私は同様の問題を抱えていて、回避策を見つけました:

私は次のクラスを持っていました:

class CommonInfo(models.Model):
    image = models.ImageField(blank=True, null=True, default="")

    class Meta:
        abstract = True

class Year(CommonInfo):
    year = models.IntegerField() 

しかし、スーパークラスの画像フィールドをnull許容に保ちながら、Yearの継承された画像フィールドが必要になるようにしたかったのです。最後に、ModelFormsを使用して、検証段階で画像を適用しました。

class YearForm(ModelForm):
    class Meta:
        model = Year

    def clean(self):
        if not self.cleaned_data['image'] or len(self.cleaned_data['image'])==0:
            raise ValidationError("Please provide an image.")

        return self.cleaned_data

admin.py:

class YearAdmin(admin.ModelAdmin):
    form = YearForm

これは一部の状況にのみ適用できるようです(確かに、サブクラスフィールドに対してより厳しいルールを適用する必要がある場合)。

clean_<fieldname>()または、の代わりにメソッドを使用することもできますclean()。たとえば、フィールドtownに入力する必要がある場合は、次のようにします。

def clean_town(self):
    town = self.cleaned_data["town"]
    if not town or len(town) == 0:
        raise forms.ValidationError("Please enter a town")
    return town
于 2011-12-15T16:07:21.790 に答える
1

モデルフィールドをオーバーライドすることはできませんが、clean()メソッドをオーバーライド/指定することで簡単に実現できます。メールフィールドに問題があり、モデルレベルで一意にしたいと思い、次のようにしました。

def clean(self):
    """
    Make sure that email field is unique
    """
    if MyUser.objects.filter(email=self.email):
        raise ValidationError({'email': _('This email is already in use')})

エラーメッセージは、「email」という名前のフォームフィールドによってキャプチャされます。

于 2015-06-23T22:13:29.597 に答える