django是一个web框架,是一套用于帮助开发交互式网站的工具。和flask一样,Django采用了MVC的软件设计模式,即模型M,视图V和控制器C。
使用的环境:ubuntu18.04+Django2.1.2+python3.6,pycharm集成开发平台。
使用python的venv模块创建虚拟模块。也可以在pycharm中创建project时选择使用virtualenv虚拟环境。
$ sudo apt install python3-venv
使用venv模块创建虚拟环境。
$ python3 -m venv venv
激活虚拟环境,运行activate脚本。
$ source venv/bin/activate
环境处于活动状态时,环境名包含(venv)标识。执行deactivate
命令,停止使用虚拟环境
(venv) ulysses@ulysses:~/PycharmProjects/django_ulysses$ deactivate
ulysses@ulysses:~/PycharmProjects/django_ulysses$
安装Django: pip install django
在虚拟环境下运行 django-admin startproject learning_log
创建learning_log项目,Django创建了一个名为manage.py的文件。它接受命令并将其交给Django的相关部分去执行。
(venv) ulysses@ulysses:~/PycharmProjects/django_ulysses$ ls
learning_log_django manage.py venv
查看django-admin的源码:
#!/usr/bin/env python
from django.core import management
if __name__ == "__main__":
management.execute_from_command_line()
表明django-admin.py是从命令行接受命令并执行,查看其所有命令:
ulysses@ulysses:~/PycharmProjects/django_ulysses$ django-admin.py
Type 'django-admin.py help ' for help on a specific subcommand.
Available subcommands:
[django]
check
compilemessages
createcachetable
dbshell
diffsettings
dumpdata
flush
inspectdb
...省略...
在创建的manage.py中:
#!/usr/bin/env python
import os
import sys
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'learning_log_django.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
可以看到也是使用execute_from_command_line()
来执行命令。
在创建的项目文件中包含四个文件,
使用python3 manage.py migrate
命令为项目创建数据库,Django会为我们创建一个名为db.sqlite3的文件,SQLite是一种使用单个文件的数据库。之后数据库发生修改后,运行mkmigrations
命令进行迁移数据库。
执行runserver命令
ulysses@ulysses:~/PycharmProjects/django_ulysses$ python3 manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
October 19, 2018 - 06:51:07
Django version 2.1.2, using settings 'learning_log_django.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
可以看到项目使用的配置文件learning_log_django.settings。项目在本机的8000端口上监听请求。
在项目中运行startapp命令创建名为learning_logs的应用,Django会创建一些基础设施。
(venv) ulysses@ulysses:~/PycharmProjects/django_ulysses$ python3 manage.py startapp learning_logs
(venv) ulysses@ulysses:~/PycharmProjects/django_ulysses$ cd learning_logs
(venv) ulysses@ulysses:~/PycharmProjects/django_ulysses/learning_logs$ ls
admin.py apps.py __init__.py migrations models.py tests.py views.py
在model.py创建应用的模型Topic:CharField
对应sql中的varchar
类型DateTimeField
就对于sql中的datetime
类型
from django.db import models
# Create your models here.
class Topic(models.Model):
text = models.CharField(max_length=20)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.text
在flask中使用flask-sqlalchemy扩展建立模型,而在Django中django.db.models提供了高层的ORM来建立模型。通常一个模型类就映射数据库中的一张数据表。
django.db.models所有映射的数据类型:
__all__ = [
'AutoField', 'BLANK_CHOICE_DASH', 'BigAutoField', 'BigIntegerField',
'BinaryField', 'BooleanField', 'CharField', 'CommaSeparatedIntegerField',
'DateField', 'DateTimeField', 'DecimalField', 'DurationField',
'EmailField', 'Empty', 'Field', 'FieldDoesNotExist', 'FilePathField',
'FloatField', 'GenericIPAddressField', 'IPAddressField', 'IntegerField',
'NOT_PROVIDED', 'NullBooleanField', 'PositiveIntegerField',
'PositiveSmallIntegerField', 'SlugField', 'SmallIntegerField', 'TextField',
'TimeField', 'URLField', 'UUIDField',
]
要使用创建的模型需要在上文提到的setting.py
中添加新创建的应用。
将创建的learning_logs应用添加到INSTALLED_APPS
列表中。
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 创建的应用
'learning_logs'
]
创建新模型后创建一个数据迁移:
(venv) ulysses@ulysses:~/PycharmProjects/django_ulysses$ python3 manage.py makemigrations learning_logs
Migrations for 'learning_logs':
learning_logs/migrations/0001_initial.py
- Create model Topic
在learning_logs/migrations目录下会生成对应的0001_initial.py的迁移文件,
它会在数据库中生成名为Topic的数据表。在模型中没有声名主键(primary_key=True
)时,Django自动为这个表添加了一个id字段并声名为主键。
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Topic',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.CharField(max_length=20)),
('date_added', models.DateTimeField(auto_now_add=True)),
],
),
]
之后应用这个迁移,执行migrate
命令Django会修改对应的数据库:
(venv) ulysses@ulysses:~/PycharmProjects/django_ulysses$ python3 manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
Applying learning_logs.0001_initial... OK
修改数据库的过程:
python manage.py makemigrations [appname]
python manage.py migrate
Django最为强大的部分就是自带了一个高度自动化的admin site。在setting.py 的INSTALLED_APPS可以看到项目已经包含了admin应用模块:
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
在urls.py中已经添加了admin的路由:
urlpatterns = [
path('admin/', admin.site.urls),
]
runserver
启动应用,访问http://127.0.0.1:8000/admin/, 可以看到Django自带的admin site网站管理。
权限决定了用户可执行的操作,超级用户具有所有权限。
使用createsuperuser
命令创建超级用户
(venv) ulysses@ulysses:~/PycharmProjects/django_ulysses$ python3 manage.py createsuperuser
Username (leave blank to use 'ulysses'):
Email address:
Password:
Password (again):
可以使用changepassword username
修改用户的密码。
Django自动在管理网站添加了User和Group,为了让 admin 界面管理应用创建的数据模型,需要先注册该数据模型到 admin。在应用目录下的admin.py中注册模型Topic:
from django.contrib import admin
from .models import Topic
# Register your models here.
admin.site.register(Topic)
处于development 模式下修改文件后,Django会自动重启应用。在admin界面可以看到新添加的Topic模块:
点击add
添加新的topic,在管理页面上可以修改或删除Topic。
同一Topic下发表生成不同的Post,每篇文章都要与特定的主题相联。
class Post(models.Model):
"""同一Topic下的多篇文章"""
# many to one relationship
topic = models.ForeignKey(Topic, on_delete=models.CASCADE, verbose_name='the related topic')
text = models.TextField('post content')
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'posts'
def __str__(self):
return self.text[:50] + '...'
每个字段(除ForeignKey
,ManyToManyField
,OneToOneField
外 )的第一个位置都可以添加一条冗余名称verbose_name
,没有设置时默认为字段名称。Django提供了3种最常用的数据表关系:
多
的那端)使用models.ForeignKey
,指定与之关联的那个表和字段(类);ManyToManyField (, through='Membership')
来指定一个intermediary model
关系表来连接课程与学生的关系。OneToOneField
Meta
,存储用于管理模型的额外信息,如verbose_name
的复数表现形式verbose_name_plural
,用于排序的ordering
。Django提供了交互式终端会话shell
,以编程的方式查看数据或运行指令。
(venv) ulysses@ulysses:~/PycharmProjects/django_ulysses$ python3 manage.py shellPython 3.6.6 (default, Sep 12 2018, 18:26:19)
[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from learning_logs.models import Topic
>>> Topic.objects.all()
]>
>>> Topic.objects.create(text='古典艺术')
>>> Topic.objects.get(text='古典艺术')
在admin site界面下可以看到数据被添加了
QuerySet API:
1. Person.objects.create(name=name,age=age)
2. p = Person(name="WZ", age=23)
p.save()
3. p = Person(name="TWZ")
p.age = 23
p.save()
4. Person.objects.get_or_create(name="WZT", age=23)
这种方法是防止重复很好的方法,但是速度要相对慢些,返回一个元组,第一个为Person对象,第二个为True或False, 新建时返回的是True, 已经存在时返回False.
1. Person.objects.all() # 获取所有对象
2. Person.objects.all()[:10] # 切片操作,获取10个人,不支持负索引,切片可以节约内存
3. Person.objects.get(name=name) # 根据指定条件索引对象
1. Person.objects.filter(name="abc") # 等于
2. Person.objects.filter(name__exact="abc") #名称严格等于 "abc" 的人
3. Person.objects.filter(name__iexact="abc") # 名称为 abc 但是不区分大小写,可以找到 ABC, Abc, aBC,这些都符合条件
4. Person.objects.filter(name__contains="abc") # 名称中包含 "abc"的人
5. Person.objects.filter(name__icontains="abc") #名称中包含 "abc",且abc不区分大小写
6. Person.objects.filter(name__regex="^abc") # 正则表达式查询
7. Person.objects.filter(name__iregex="^abc") # 正则表达式不区分大小写
执行select distinct
语句
1. >>> Author.objects.distinct()
[...]
2. >>> Entry.objects.order_by('author', 'pub_date').distinct('author')
[...]
1. Person.objects.exclude(name__contains="WZ") # 排除包含 WZ 的Person对象
2. Person.objects.filter(name__contains="abc").exclude(age=23) # 找出名称含有abc, 但是排除年龄是23岁的
Author.objects.all().order_by('name')
Author.objects.all().order_by('-name') # 在 column name 前加一个负号,可以实现倒序
QuerySet不支持负索引
1. 使用 reverse() 解决
Person.objects.all().reverse()[:2] # 最后两条
Person.objects.all().reverse()[0] # 最后一条
2. 使用 order_by,在栏目名(column name)前加一个负号
Author.objects.order_by('-id')[:20] # id最大的20条
删除符合条件的结果:
Person.objects.filter(name__contains="abc").delete() # 删除 名称中包含 "abc"的人
或
people = Person.objects.filter(name__contains="abc")
people.delete()
更新某个内容:
(1) 批量更新,适用于 .all() .filter() .exclude() 等后面 (危险操作,正式场合操作务必谨慎)
Person.objects.filter(name__contains="abc").update(name='xxx') # 名称中包含 "abc"的人 都改成 xxx
Person.objects.all().delete() # 删除所有 Person 记录
(2) 单个 object 更新,适合于 .get(), get_or_create(), update_or_create() 等得到的 obj,和新建很类似。
twz = Author.objects.get(name="WeizhongTu")
twz.name="WeizhongTu"
twz.email="[email protected]"
twz.save()
获取所有字段的值,执行select *
语句:
# This list contains a Blog object.
>>> Blog.objects.filter(name__startswith='Beatles')
<QuerySet [<Blog: Beatles Blog>]>
# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith='Beatles').values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
使用提供的查询表达式列表注释QuerySet中的每个对象。annotate()的每个参数都是一个注释,将添加到返回的QuerySet中的每个对象。
>>> from django.db.models import Count
>>> q = Topic.objects.annotate(Count('post'))
# The name of the first topic
>>> q[0].text
'天-剑'
# The number of posts on the first topic
>>> q[0].post__count
1
>>> q[3].post__count
0
在应用下创建 fake.py,使用faker模块自动生成数据
"""用于生成虚拟topic 和 post"""
from random import randint
from faker import Faker
from .models import Topic, Post
def topics(count=100):
print(f"count={count}")
fake = Faker()
i = 0
while i < count:
t = Topic(id=i, text=fake.text())
t.save()
i = i + 1
def posts(count=100):
fake = Faker()
i = 0
topic_count = Topic.objects.count()
for i in range(count):
# 在随机Topic下生成Post
count_1 = randint(0, topic_count-1)
print(f"topic_id:{count_1}")
t = Topic.objects.get(id=count_1)
print(t)
p = Post(id=i, text=fake.text(), topic=t)
p.save()
为通过外键获取数据,可使用相关模型的小写名称,下划线和单词set:
>>> t = Topic.objects.get(id=8)
>>> t.post_set.all()
, , ]>
>>>
在管理界面可以看到自动生成的数据:
部分内容参考https://code.ziqiangxuetang.com/django/django-tutorial.html