Django开发指南

作者:刘宾, thomas_liub@hotmail.com
请尊重作者著作权,转载请注明出处,谢谢!


数据模型

字段

1. 数据类型

Django内置数据类型

2. 字段选项

  • null = True
    Django存储NULL作为空值,缺省False

  • blank = True
    Django允许该值为空,缺省False

|Field类型| 设置null=True| 设置blank=True|
|- |- | - |- |
|CharField
TextField
SlugField
EmailField
CommaSeparatedIntegerField等|不要设置
django规定储存空字符串来代表空值, 当从数据库中读取NULL或空值时都为空字符串|可以设置
设置后允许接受widget中为空值(即不填写), 储存到数据库时空值变为空字符串|
|FileField
ImageField|不要设置
django实际储存的是路径的字符串, 因此同上| 可以设置
同上|
|BooleanField|不要设置
因为有NullBooleanField代替|不要设置|
|IntegerField
FloatField
DecimalField等|可以设置
如果你希望在数据库中能储存NULL|可以设置
设置后允许接受widget中为空值(即不填写), 设置为True时必须同时设置null=True|
|DateTimeField
DateField
TimeField等|可以设置
如果你希望在数据库中能储存NULL|可以设置
设置后允许接受widget中为空值(即不填写), 设置为True时必须同时设置null=True|
|ForeignKey
ManyToManyField
OneToOneField|可以设置
如果你希望在数据库中能储存NULL| 可以设置
设置后允许接受widget中为空值(即不填写)|
|GenericIPAddressField|可以设置
如果你希望在数据库中能储存NULL|可以设置
设置后允许接受widget中为空值(即不填写)|
|IPAddressField|不推荐设置
用GenericIPAddressField代替|不推荐设置
用GenericIPAddressField代替|

  • choice类型
    第一列为数据库中实际存储的内容。
class Person(models.Model):
    SHIRT_SIZES = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
  • default
    设置缺省值

  • primary_key = True
    设置该字段为模型主键

  • unique = True
    设置该字段表内唯一

3. 字段名称

first_name = models.CharField(verbose_name="person's first name", max_length=30)

4. 关系

on_delete和on_update设置为:models.CASCADE, models.SET_NULL, models.SET_DEFAULT, models.DO_NOTHING
models.CASCADE: 级联删除或更新(默认值)
models.SET_NULL:设置成NULL
models.SET_DEFAULT: 设置成default值
models.DO_NOTHING: 什么也不做

  • Many-to-one关系,即外键
    外键可以递归指向自己。可以定义on_delete
class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer,  verbose_name="field name", on_delete=models.CASCADE)
  • Many-to-mangy关系
    多对多关系可以递归指向自己。
class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

用户定义关联表

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):              # __unicode__ on Python 2
        return self.name
class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __str__(self):              # __unicode__ on Python 2
        return self.name
class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)
  • One-to-one关系
    可以定义on_delete
class MySpecialUser(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )
    supervisor = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='supervisor_of',
    )

模型重载

将业务逻辑保留在模型层面是优良的设计风格。

1. 扩展model字段

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()

    def baby_boomer_status(self):
        "Returns the person's baby-boomer status."
        import datetime
        if self.birth_date < datetime.date(1945, 8, 1):
            return "Pre-boomer"
        elif self.birth_date < datetime.date(1965, 1, 1):
            return "Baby boomer"
        else:
            return "Post-boomer"

    @property
    def full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)

2. 重载已经定义方法

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        do_something()
        super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
        do_something_else()

模型继承

用户需要考虑是否允许父模型创建表格,还是仅仅保留common信息,全部在子模型中创建表格。

1. 抽象继承

abstract = True

class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True

class Student(CommonInfo):
    home_group = models.CharField(max_length=5)

2. 多表继承

通过自动追加一个OneToOneField

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

3. proxy model

重载父模型中某些方法,不创建子表格。
proxy = True

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class MyPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        # ...
        pass

view处理

事务处理

1. 缺省在autocommit模式

ATOMIC_REQUESTS = True,每个http req都作为一个事务处理,需要排除非事务处理部分。

  • 不做数据修改的操作, 应当排除在transaction之外
  • 做数据修改的操作, 则应在transaction内
  • 特殊情况下, 可以违反以上两条
DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql_psycopg2',
            'NAME': '',
            'USER': '',
            'PASSWORD': '',
            'HOST': '',
            'PORT': '',
            'ATOMIC_REQUESTS': True,
        }
    }
    @transaction.non_atomic_requests # 以下http request不被包裹在一个transaction中
    def do_something_to_article(request, pk, title):
        article = get_object_or_404(Article, pk=pk)

        # 以下代码会以django默认的autocommit模式执行
        article.datetime = timezone.now()
        article.save()

        with transaction.atomic()
            # 以下代码被包裹在另一个transaction中
            article.title = title
            article.datetime = timezone.now()
            article.save()
            return HttpResponse("success!")

        # 如果以上transaction失败了, 返回错误状态
        return HttpResponse("oops! failed", status_code=400)

2. 修饰view事务处理

@transaction.atomic
def viewfunc(request):
    # This code executes inside a transaction.
    do_stuff()

关系的定义和使用

class Asset(models.Model):
    asset_model = models.ForeignKey(AssetModel, related_name='assets')
    asset_groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets')
    ...
    operator_group = models.OneToOneField(Group, related_name='asset')
  • 从另外端访问所有设备
    asset_model_obj.assets.all()
    asset_group_obj.assets.all()
    group_obj.asset

  • 从asset访问另外端
    asset_obj.asset_modle
    asset_obj.asset_groups
    asset_obj.operator_group

  • 添加/删除关系
    asset_model_obj.assets.add/remove(asset_obj1, asset_obj2,...)
    asset_groups_obj.assets.add/remove(sset_obj1, asset_obj2,...)

  • 清除关系
    asset_model_obj.assets.clear()
    asset_groups_obj.assets.clear()

对象操作

1. 对象查询

  • 查询
    Asset.objects.all()
    Asset.objects.filter()
    Asset.objects.exclude()
    Asset.objects.get()
    Asset.objects.filter().values()
    < QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}] >
    Asset.objects.filter().values_list()
    [(1, 'first entry'), ...]
    Asset.objects.extra()
    entries = Entry.objects.select_for_update().filter(author=request.user)
  • 计数,分片和排序,count(),[], order_by(), revise()
    Asset.objects.filter(sn=assetSn, did=dataitemID).count()
    Asset.objects.filter().order_by('blog__name')[:5] //first 5
    Asset.objects.filter().order_by('-blog__name', 'headline')[5:10] // 6-10
    Asset.objects.filter().order_by('-blog__name', 'headline').revise() //反序排列
  • 高级查询
    Asset.objects.filter(Q(question__startswith='Who'), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))

2. 对象创建

b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
b.save()

3. 对象删除

obj_queryset.delete()
or
asset_obj.delete()

4. 运算

Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

5. 字符串查询

Author.objects.filter(name__unaccent__icontains='Helen')
Author.objects.filter(name__contains='Terry')

Serializer

参考Django REST Serializer

1. StringRelatedField

unicode方法表示只读关系,
参数:

  • many = True
class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.StringRelatedField(many=True)
    class Meta:
        model = Album
        fields = ('album_name', 'artist', 'tracks')
{
    'album_name': 'Things We Lost In The Fire',
    'artist': 'Low',
    'tracks': [
        '1: Sunflower',
        '2: Whitetail',
        '3: Dinosaur Act',
        ...
    ]
}

2. PrimaryKeyRelatedField

用主键表示关系,
参数:

  • queryset - The queryset used for model instance lookups when validating the field input. Relationships must either set a queryset explicitly, or set read_only=True.
  • many - If applied to a to-many relationship, you should set this argument to True.
  • allow_null - If set to True, the field will accept values of None or the empty string for nullable relationships. Defaults to False.
  • pk_field - Set to a field to control serialization/deserialization of the primary key's value. For example, pk_field=UUIDField(format='hex') would serialize a UUID primary key into its compact hex representation.
class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
    class Meta:
        model = Album
        fields = ('album_name', 'artist', 'tracks')
{
    'album_name': 'Undun',
    'artist': 'The Roots',
    'tracks': [
        89,
        90,
        91,
        ...
    ]
}

3. SlugRelatedField

选取关系对象中任意一个字段(唯一标识)表示关系
参数:

  • slug_field - The field on the target that should be used to represent it. This should be a field that uniquely identifies any given instance. For example, username. required
  • queryset - The queryset used for model instance lookups when validating the field input. Relationships must either set a queryset explicitly, or set read_only=True.
  • many - If applied to a to-many relationship, you should set this argument to True.
  • allow_null - If set to True, the field will accept values of None or the empty string for nullable relationships. Defaults to False.
class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.SlugRelatedField(
        many=True,
        read_only=True,
        slug_field='title'
     )
    class Meta:
        model = Album
        fields = ('album_name', 'artist', 'tracks')
{
    'album_name': 'Dear John',
    'artist': 'Loney Dear',
    'tracks': [
        'Airport Surroundings',
        'Everything Turns to You',
        'I Was Only Going Out',
        ...
    ]
}

4. Nested relationships

嵌套定义

  • 只读serializer:
class TrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = ('order', 'title', 'duration')
class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackSerializer(many=True, read_only=True)

    class Meta:
        model = Album
        fields = ('album_name', 'artist', 'tracks')
{
    'album_name': 'The Grey Album',
    'artist': 'Danger Mouse',
    'tracks': [
        {'order': 1, 'title': 'Public Service Announcement', 'duration': 245},
        {'order': 2, 'title': 'What More Can I Say', 'duration': 264},
        {'order': 3, 'title': 'Encore', 'duration': 159},
        ...
    ],
}
  • 可写Serializer:
    在写子对象时,父对象不一定存在,涉及父对象的创建。创建父对象+创建子对象+创建关系。
class TrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = ('order', 'title', 'duration')
class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackSerializer(many=True)

    class Meta:
        model = Album
        fields = ('album_name', 'artist', 'tracks')

    def create(self, validated_data):
        tracks_data = validated_data.pop('tracks')
        album = Album.objects.create(**validated_data)
        for track_data in tracks_data:
            Track.objects.create(album=album, **track_data)
        return album
>>> data = {
    'album_name': 'The Grey Album',
    'artist': 'Danger Mouse',
    'tracks': [
        {'order': 1, 'title': 'Public Service Announcement', 'duration': 245},
        {'order': 2, 'title': 'What More Can I Say', 'duration': 264},
        {'order': 3, 'title': 'Encore', 'duration': 159},
    ],
}
>>> serializer = AlbumSerializer(data=data)
>>> serializer.is_valid()
True
>>> serializer.save()
<Album: Album object>

5. 用户自定义字段

在写子对象时,父对象已存在,不涉及父对象的创建,需要做:创建或更新子对象+创建或更新关系

class ModField(serializers.RelatedField):
    def to_representation(self, value):
        """
        output to read
        """
        return value.name

    def to_internal_value(self, value):
        """
        input to write, value = org, model name
        """
        targetOrg = Org.objects.get(name=value['org'])
        return AssetModel.objects.filter(org=targetOrg).get(name=value['mod'])

class DataitemSerializer(serializers.ModelSerializer):
    asset_model = ModField(queryset=AssetModel.objects.all())
    class Meta:
        model = Dataitem
        fields = ('asset_model', 'name', 'data_type', 'max', 'min', 'step', 'storage', 'update_interval',
                  'retention_history', 'retention_period', 'writeable', 'description', 'instance_table')

类view和函数view

Django函数view装饰器

@require_http_methods(["GET", "POST"])

Django REST函数view装饰器,@api_view

@api_view(['GET', 'POST'], exclude_from_schema=True)
def hello_world(request):
    if request.method == 'POST':
        return Response({"message": "Got some data!", "data": request.data})
    return Response({"message": "Hello, world!"})

Django REST APIView是Django class view的子类

参考

Django DOC
Django REST DOC

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容