Django

#python #web

快速上手

  1. 创建项目

     django-admin startproject test
    
  • 创建应用

      django-admin startapp hello
    
  • 安装应用

    test/settings.py中的INSTALLED_APPS增加hello

      INSTALLED_APPS = (
          'django.contrib.admin',
          'django.contrib.auth',
          'django.contrib.contenttypes',
          'django.contrib.sessions',
          'django.contrib.messages',
          'django.contrib.staticfiles',
          'hello',
      )
    
  • 处理请求

    hello/views.py中增加如下内容:

      from django.http import HttpResponse
        
      def home(request):
          return HttpResponse("Hello Django!")
    
  • 增加路由

    test/urls.py中增加如下内容:

      urlpatterns = [
          url(r'^$', 'hello.views.home'),
            
          url(r'^admin/', include(admin.site.urls)),
      ]
    
  • 启动服务

      python manage.py runserver
    

Model

ORM抽象层,可将Model自动映射成数据库中的表。

Field(字段)

一个Field就对应数据库中的一个字段(或列)。
Field Type可自动映射为数据库中字段的类型;如果使用Form,也可自动生成相应的表单以及作相应的表单验证。
Field Options是传递给Field的参数,可用于指定数据库列的约束、表单验证等。

null与blank

null作用于数据库。null=True,表示数据库中该列可为NULL,上层接口不传实参(无缺省值情况下)或者实参为None到数据库中都是NULL。
blank作用于表单验证。blank=True,表示表单中该字段不是必须填的(可见admin页面)。

关系

Many-to-one

Model:

class Manufacturer(models.Model):
    pass

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer)

SQLite:

CREATE TABLE "relationships_manufacturer" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT);
CREATE TABLE "relationships_car" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, \
                                  "manufacturer_id" integer NOT NULL REFERENCES "relationships_manufacturer" ("id"));
CREATE INDEX "relationships_car_4d136c4a" ON "relationships_car" ("manufacturer_id");

就是普通的外键,manufacturer映射成manufacturer_id

Many-to-many

Model:

class Topping(models.Model):
    pass

class Pizza(models.Model):
    toppings = models.ManyToManyField(Topping)

SQLite:

CREATE TABLE "relationships_pizza" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT);
CREATE TABLE "relationships_topping" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT);
CREATE TABLE "relationships_pizza_toppings" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, \
                                             "pizza_id" integer NOT NULL REFERENCES "relationships_pizza" ("id"), \
                                             "topping_id" integer NOT NULL REFERENCES "relationships_topping" ("id"), \
                                             UNIQUE ("pizza_id", "topping_id"));
CREATE INDEX "relationships_pizza_toppings_c3c2621e" ON "relationships_pizza_toppings" ("pizza_id");
CREATE INDEX "relationships_pizza_toppings_3a22b0a0" ON "relationships_pizza_toppings" ("topping_id");

toppings非数据库字段,而是映射成一张关系表pizza_toppings(注意Unique约束)。

操作:

t1 = Topping()
t1.save()
p1 = Pizza()
p1.save()
p1.toppings.add(t1)

p1.toppings.create()      # create and add

p1.toppings.all()
t1.pizza_set.all()        # xxx_set

Toppings.objects.get(id=2).pizza_set.all()

t1.delete()               # 关系也会清除
p1.delete()               # 关系也会清除

p1.toppings.clear()       # 清除关系
t1.pizza_set.clear()      # 清除关系
p1.toppings = []          # 清除关系
t1.pizza_set = []         # 清除关系

Extra fields on many-to-many

Model:

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)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

SQLite:

CREATE TABLE "relationships_person" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(128) NOT NULL);
CREATE TABLE "relationships_group" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(128) NOT NULL);
CREATE TABLE "relationships_membership" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, \
                                         "date_joined" date NOT NULL, \
                                         "invite_reason" varchar(64) NOT NULL, \
                                         "group_id" integer NOT NULL REFERENCES "relationships_group" ("id"), \
                                         "person_id" integer NOT NULL REFERENCES "relationships_person" ("id"));
CREATE INDEX "relationships_membership_0e939a4f" ON "relationships_membership" ("group_id");
CREATE INDEX "relationships_membership_a8452ca7" ON "relationships_membership" ("person_id");

参数through用来指定关系表Membership,而非使用按规则自动创建的group_memebers表,这样就可以往关系表中增加额外字段。

操作:

grp1.members.add(john)              # Error
grp1.members.create(name='John')    # Error
grp1.members = [john, paul]         # Error
grp1.memebers.clear()               # 清除所有grp1的关系

不能使用普通many-to-many里面的add,create,=等操作,官方文档的说法是信息量太少,它搞不定了。。。

One-to-one

Model:

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

    def __str__(self):              # __unicode__ on Python 2
        return "%s the place" % self.name

class Restaurant(models.Model):
    place = models.OneToOneField(Place, primary_key=True)
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)
    place_2 = models.OneToOneField(Place)

    def __str__(self):              # __unicode__ on Python 2
        return "%s the restaurant" % self.place.name

class Waiter(models.Model):
    restaurant = models.ForeignKey(Restaurant)
    name = models.CharField(max_length=50)

    def __str__(self):              # __unicode__ on Python 2
        return "%s the waiter at %s" % (self.name, self.restaurant)

SQLite:

CREATE TABLE "relationships_place" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(50) NOT NULL, "address" varchar(80) NOT NULL);
CREATE TABLE "relationships_restaurant" ("place_id" integer NOT NULL PRIMARY KEY REFERENCES "relationships_place" ("id"), "serves_hot_dogs" bool NOT NULL, "serves_pizza" bool NOT NULL);
CREATE TABLE "relationships_waiter" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(50) NOT NULL, "restaurant_id" integer NOT NULL REFERENCES "relationships_restaurant" ("place_id"));
CREATE INDEX "relationships_waiter_ee9d9d3e" ON "relationships_waiter" ("restaurant_id");

One-to-one = ForeignKey + (unique=True or primary_key=True)

操作:

r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False); r.save()
r.place
p1.restaurant
hasattr(p1, 'restaurant')
r.place = p2; r.save()
p1.restaurant = r
w = r.waiter_set.create(name='Joe'); w.save()

自引用

models.ForeignKey('self')
models.ManyToManyField('self')

自定义主键

缺省情况下,会自动生成主键:

CREATE TABLE "hello_person" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, ...);

也可以自定义,指定primary_key=True即可:

id = models.AutoField(primary_key=True)

数据库导入、导出

  • 从文件导入

    python manage.py loaddata init.yaml     # 可以是json,xml,yaml格式
    
  • 从默认文件导入

    执行migrate时会自动读取initial_data.[xml/yaml/json]文件并导入。

    首次migrate要加参数(因为表还没建,无法导入数据),否则会报错:

    python manage.py migrate --no-initial-data
    
  • Data Migrations

    文件导入的方法已废弃,但在Django 1.8.2中还可以工作。 1.9开始应该就只能用Migrations来导入数据了。

    首先,创建一个新的migration文件模板,比如migrations/0004_initial_data.py

    python manage.py makemigrations --empty hello -n initial_data
    

    然后,编辑成如下:

    def load_data(apps, schema_editor):
        Person = apps.get_model("hello", "Person")
        db_alias = schema_editor.connection.alias
        Person.objects.using(db_alias).bulk_create([
            Person(first_name="Michael", last_name="Jordan"),
            Person(first_name="Kobe", last_name="Bryant"),
            Person(first_name="LeBron", last_name="James"),
        ])
      
    class Migration(migrations.Migration):
      
        dependencies = [
            ('hello', '0003_person_addr'),
        ]
      
        operations = [
            migrations.RunPython(
                load_data,
            ),
        ]
    

    最后,执行migrate即可。

Why Data Migrations?

这样就不会出现Model更新后,初始化数据和Model不匹配的问题。

  • 导出

    python manage.py dumpdata                             # 导出全部
    python manage.py dumpdata APP                         # 仅导出app
    python manage.py dumpdata APP.MODEL                   # 仅导出app.model
    python manage.py dumpdata APP.MODEL --format yaml     # 默认json格式