• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
不哭不闹,不吵不笑
博客园    首页    新随笔    联系   管理    订阅  订阅

Python攻关之Django(一)转自:::http://www.cnblogs.com/zhangqigao/p/5800906.html

 课程简介:

  • Django流程介绍
  • Django url
  • Django view
  • Django models
  • Django template
  • Django form
  • Django admin (后台数据库管理工具)

 

1 Django流程介绍

MTV模式      

        著名的MVC模式:所谓MVC就是把web应用分为模型(M),控制器(C),视图(V)三层;他们之间以一种插件似的,松耦合的方式连接在一起。

        模型负责业务对象与数据库的对象(ORM),视图负责与用户的交互(页面),控制器(C)接受用户的输入调用模型和视图完成用户的请求。

        

       Django的MTV模式本质上与MVC模式没有什么差别,也是各组件之间为了保持松耦合关系,只是定义上有些许不同,Django的MTV分别代表:

       Model(模型):负责业务对象与数据库的对象(ORM)

       Template(模版):负责如何把页面展示给用户

       View(视图):负责业务逻辑,并在适当的时候调用Model和Template

       此外,Django还有一个url分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template

 

2 Django URL

URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL模式以及要为该URL模式调用的视图函数之间的映射表;你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。URL的家在是从配置文件中开始。

参数说明:

  • 一个正则表达式字符串
  • 一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
  • 可选的要传递给视图函数的默认参数(字典形式)
  • 一个可选的name参数

2.1  Here’s a sample URLconf:

1
2
3
4
5
6
7
8
9
10
from django.conf.urls import url
  
from . import views
  
urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/([0-9]{4})/$', views.year_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]

Notes:

  • To capture a value from the URL, just put parenthesis around it.
  • There’s no need to add a leading slash, because every URL has that. For example, it’s ^articles, not ^/articles.
  • The 'r' in front of each regular expression string is optional but recommended. It tells Python that a string is “raw” – that nothing in the string should be escaped. See Dive Into Python’s explanation.

Example requests:

  • A request to /articles/2005/03/ would match the third entry in the list. Django would call the functionviews.month_archive(request, '2005', '03').
  • /articles/2005/3/ would not match any URL patterns, because the third entry in the list requires two digits for the month.
  • /articles/2003/ would match the first pattern in the list, not the second one, because the patterns are tested in order, and the first one is the first test to pass. Feel free to exploit the ordering to insert special cases like this. Here, Django would call the function views.special_case_2003(request)
  • /articles/2003 would not match any of these patterns, because each pattern requires that the URL end with a slash.
  • /articles/2003/03/03/ would match the final pattern. Django would call the functionviews.article_detail(request, '2003', '03', '03').

2.2 Named groups

The above example used simple, non-named regular-expression groups (via parenthesis) to capture bits of the URL and pass them as positional arguments to a view. In more advanced usage, it’s possible to use named regular-expression groups to capture URL bits and pass them as keyword arguments to a view.

In Python regular expressions, the syntax for named regular-expression groups is (?P<name>pattern), where name is the name of the group and pattern is some pattern to match.

Here’s the above example URLconf, rewritten to use named groups:

 正则知识
1
2
3
4
5
6
7
8
9
10
from django.conf.urls import url
  
from . import views
  
urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]

This accomplishes exactly the same thing as the previous example, with one subtle difference: The captured values are passed to view functions as keyword arguments rather than positional arguments. For example:

  • A request to /articles/2005/03/ would call the function views.month_archive(request, year='2005',month='03'), instead of views.month_archive(request, '2005', '03').
  • A request to /articles/2003/03/03/ would call the function views.article_detail(request, year='2003',month='03', day='03').

In practice, this means your URLconfs are slightly more explicit and less prone to argument-order bugs – and you can reorder the arguments in your views’ function definitions. Of course, these benefits come at the cost of brevity; some developers find the named-group syntax ugly and too verbose.

常见写法实例:

2.3  Captured arguments are always strings

Each captured argument is sent to the view as a plain Python string, regardless of what sort of match the regular expression makes. For example, in this URLconf line:

1
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
...the year argument passed to views.year_archive() will be a string,
not an integer, even though the [0-9]{4} will only match integer strings.

2.4  Including other URLconfs 

At any point, your urlpatterns can “include” other URLconf modules. This essentially “roots” a set of URLs below other ones.

For example, here’s an excerpt of the URLconf for the Django website itself. It includes a number of other URLconfs:

1
2
3
4
5
6
7
8
from django.conf.urls import include, url
  
urlpatterns = [
    # ... snip ...
    url(r'^community/', include('django_website.aggregator.urls')),
    url(r'^contact/', include('django_website.contact.urls')),
    # ... snip ...
]

2.5 Passing extra options to view functions

URLconfs have a hook that lets you pass extra arguments to your view functions, as a Python dictionary.

The django.conf.urls.url() function can take an optional third argument which should be a dictionary of extra keyword arguments to pass to the view function.

For example:

1
2
3
4
5
6
from django.conf.urls import url
from . import views
  
urlpatterns = [
    url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
]

In this example, for a request to /blog/2005/, Django will call views.year_archive(request, year='2005',foo='bar').

This technique is used in the syndication framework to pass metadata and options to views.

Dealing with conflicts

It’s possible to have a URL pattern which captures named keyword arguments, and also passes arguments with the same names in its dictionary of extra arguments. When this happens, the arguments in the dictionary will be used instead of the arguments captured in the URL.

需要注意的是,当你加上参数时,对应函数views.index必须加上一个参数,参数名也必须命名为a,如下:

 

1
2
3
4
5
6
7
#应用:
 
if  auth():
 
    obj=model.user.filter()
 
{'obj':obj}

2.6 name param

 name的应用

3 Django Views(视图函数)

七  视图函数

http请求中产生两个核心对象:

http请求:HttpRequest对象

http响应:HttpResponse对象

所在位置:django.http

之前我们用到的参数request就是HttpRequest    检测方法:isinstance(request,HttpRequest)

 

1 HttpRequest对象的属性:

 

 modles

HttpRequest对象的方法:get_full_path(),   比如:http://127.0.0.1:8000/index33/?name=123 ,req.get_full_path()得到的结果就是/index33/?name=123

 

 2 HttpResponse对象:

   对于HttpRequest对象来说,是由django自动创建的,但是,HttpResponse对象就必须我们自己创建。每个view请求处理方法必须返回一个HttpResponse对象。

  HttpResponse类在django.http.HttpResponse

  在HttpResponse对象上扩展的常用方法:页面渲染:render,render_to_response,

                                                        页面跳转:redirect

                                                        locals:   可以直接将函数中所有的变量传给模板    

   

 

4 Django Models

4.1 数据库配置  

1      django默认支持sqlite,mysql, oracle,postgresql数据库。

    <1> sqlite

            django默认使用sqlite的数据库,默认自带sqlite的数据库驱动

            引擎名称:django.db.backends.sqlite3

     <2>mysql

            引擎名称:django.db.backends.mysql

2    mysql驱动程序

          MySQLdb(mysql python)

          mysqlclient

          MySQL

          PyMySQL(纯python的mysql驱动程序)

3     在django的项目中会默认使用sqlite数据库,在settings里有如下设置:

           

              如果我们想要更改数据库,需要修改如下:

             

 对应代码

 注意:NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建

         USER和PASSWORD分别是数据库的用户名和密码。

         设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。

         然后,启动项目,会报错:no module named MySQLdb

         这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL

         所以,我们只需要找到项目名文件下的__init__,在里面写入:

                 import pymysql

                 pymysql.install_as_MySQLdb()

         问题就解决了!

         这时就可以正常启动了。

        但此时数据库内并没有内容,我们需要做数据库的同步:

        


       为了更好的查询修改数据库,我们可以不使用Navicate,而是利用pycharm的Database(爱死pycharm啦)

       

      然后 ,安装MySQL的驱动(driver),这里需要创建一个密码(我的是123)安装成功后,

       

      填入数据库的名字,mysql的用户名和密码,然后就可以进行连接了。

       

      成功后点击右下角的apply和OK。

      这是你就可以看到数据库里的表和内容了:

       

      是不是很方便呢?

      如果你用的是sqlite数据库就更简单了,安装完驱动后,直接将sqlite拖动到Database就可以了:

      

 


 

4.2  Django的ORM(关系对象映射)

4.2.1  模型类的定义(一)

 

用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作。

 优点:1 ORM使得我们的通用数据库交互变得简单易行,而且完全不用考虑该死的SQL语句。快速开发,由此而来。

          2 可以避免一些新手程序猿写sql语句带来的性能问题。

            比如 我们查询User表中的所有字段:

            

            新手可能会用select * from  auth_user,这样会因为多了一个匹配动作而影响效率的。

 缺点:1 性能有所牺牲,不过现在的各种ORM框架都在尝试各种方法,比如缓存,延迟加载登来减轻这个问题。效果                 很显著。

           2 对于个别复杂查询,ORM仍然力不从心,为了解决这个问题,ORM一般也支持写raw sql。

 

 

下面要开始学习Django ORM语法了,为了更好的理解,我们来做一个基本的 书籍/作者/出版商 数据库结构。 我们这样做是因为 这是一个众所周知的例子,很多SQL有关的书籍也常用这个举例。

实例:我们来假定下面这些概念,字段和关系

作者模型:一个作者有姓名。

作者详细模型:把作者的详情放到详情表,包含性别,email地址和出生日期,作者详情模型和作者模型之间是一对一的关系(one-to-one)(类似于每个人和他的身份证之间的关系),在大多数情况下我们没有必要将他们拆分成两张表,这里只是引出一对一的概念。

出版商模型:出版商有名称,地址,所在城市,省,国家和网站。

书籍模型:书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many),一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many),也被称作外键。

 对应代码

记得在settings里的INSTALLED_APPS中加入'app01',然后同步数据库:

 

分析代码:

        1  每个数据模型都是django.db.models.Model的子类,它的父类Model包含了所有必要的和数据库交互的方法。并提供了一个简介漂亮的定义数据库字段的语法。

        2  每个模型相当于单个数据库表(多对多关系例外,会多生成一张关系表),每个属性也是这个表中的字段。属性名就是字段名,它的类型(例如CharField)相当于数据库的字段类型(例如varchar)。大家可以留意下其它的类型

都和数据库里的什么字段对应。

         3  模型之间的三种关系:一对一,一对多,多对多。

             一对一:实质就是在主外键(author_id就是foreign key)的关系基础上,给外键加了一个UNIQUE的属性;

           

            一对多:就是主外键关系;

                               

           

             多对多:

                     book类里定义了一个多对多的字段authors,并没在book表中,这是因为创建了一张新的表:

                                              

         4  模型的常用字段类型以及参数:

 模型的常用字段类型以及参数

4.2.1  模型类的定义(二)      

一  定义数据模型的扩展属性

     通过内部类Meta给数据模型类增加扩展属性:

     class Meta:

             verbose_name='名称'      #表名由英文转换成中文了

             verbose_name_plural='名称复数形式'

             ordering='排序字段'   

    创建超级用户后,登录admin发现我们定义的表并不在,我们需要对所创建的表(类)进行注册:

    

 

 对应代码

 

这是因为:

           

     

二  定义模型方法

    定义模型方法和定义普通python类方法没有太大的差别,定义模型方法可以将当前对应的数据组装成具体的业务逻辑。

     示例:定义__str__()让对象有一个名字

     def __str__(self):

           return self.name

当添加一个作者保存后:       

          这里只显示生成了一个作者对象,可我们希望在这里出现的是作者的名字,所以:

         

刷新页面:

    

4.3 ORM常用操作

4.3.1 增加

    create和save方法

实例:

 对应代码

注意:如果每次创建一个对象,想显示对应的raw sql,需要在settings加上日志记录部分:

 对应代码

那么如何插入存在外键和多对多关系的一本书的信息呢?

 对应代码

所有表结果如下:

          

     

     

     

 总结:

      1   objects:   model默认管理器。

      2   插入主外键关系的时候,可以用对象的方式,也可以用关联id的方式。

      3   插入多对多关系的时候要分步操作。

      4   create是管理器objects的方法

           save是model对象的方法

4.3.2 修改

    update和save方法

实例:

    

1 >>> author=Author.objects.get(id=2)
2 >>> author.name='tenglan'
3 >>> author.save()
4 >>> Publisher.objects.filter(id=2).update(name='American publisher')

注意:<1>不能用get的原因是:update是QuerySet对象的方法,get返回的是一个model对象,它没有update方法,而filter返回的是一个QuerySet对象。

         <2>  filter:

1
2
>>> Publisher.objects.filter(name__contains="press")
[   <Publisher: Apress>]

 在 name 和 contains 之间有双下划线。和Python一样,Django也使用双下划线来表明会进行一些魔术般的操作。这里,contains部分会被Django翻译成LIKE语句:

1
2
3
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name LIKE '%press%';

其他的一些查找类型有:icontains(大小写不敏感的LIKE),startswith和endswith, 还有range     

     <3> 在“插入和更新数据”小节中,我们有提到模型的save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列。例如说我们现在想要将Apress Publisher的名称由原来的”Apress”更改为”Apress Publishing”。若使用save()方法,如:

1
2
3
>>> p = Publisher.objects.get(name='Apress')
>>> p.name = 'Apress Publishing'
>>> p.save()

  这等同于如下SQL语句:

1
2
3
4
5
6
7
8
9
10
11
12
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = 'Apress';
  
UPDATE books_publisher SET
    name = 'Apress Publishing',
    address = '2855 Telegraph Ave.',
    city = 'Berkeley',
    state_province = 'CA',
    country = 'U.S.A.',
    website = 'http://www.apress.com'
WHERE id = 52;

  注意在这里我们假设Apress的ID为52)

在这个例子里我们可以看到Django的save()方法更新了不仅仅是name列的值,还有更新了所有的列。 若name以外的列有可能会被其他的进程所改动的情况下,只更改name列显然是更加明智的。 更改某一指定的列,我们可以调用结果集(QuerySet)对象的update()方法: 示例如下:

1
>>> Publisher.objects.filter(id=52).update(name='Apress Publishing')

  与之等同的SQL语句变得更高效,并且不会引起竞态条件。

1
2
3
UPDATE books_publisher
SET name = 'Apress Publishing'
WHERE id = 52;

  update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录。 以下示例演示如何将所有Publisher的country字段值由’U.S.A’更改为’USA’:

1
2
>>> Publisher.objects.all().update(country='USA')
2

  update()方法会返回一个整型数值,表示受影响的记录条数。 在上面的例子中,这个值是2。

4.3.3  查询 

>>> Publisher.objects.all()
[<Publisher: 中国机械出版社>, <Publisher: American publisher>]

注意:

这相当于这个SQL语句:

1
2
SELECT id, name, address, city, state_province, country, website
FROM books_publisher;

注意到Django在选择所有数据时并没有使用 SELECT* ,而是显式列出了所有字段。

惰性机制:

所谓惰性机制:Publisher.objects.all()只是返回了一个QuerySet(查询结果集对象),并不会马上执行sql,而是当调用QuerySet的时候才执行。

QuerySet特点:

       1   可迭代的

       2   可切片

一  查询相关API:

 <1>get(**kwargs):        返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。

 <2>all():                       查询所有结果

 <3>filter(**kwargs):      它包含了与所给筛选条件相匹配的对象

 <4>exclude(**kwargs):  它包含了与所给筛选条件不匹配的对象

 <5>order_by(*field):      对查询结果排序

 <6>reverse():                对查询结果反向排序

 <7>distinct():                从返回结果中剔除重复纪录

 <8>values(*field):         返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一

                                     系列 model的实例化对象,而是一个可迭代的字典序列

 <9>values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列

 <10>count():                返回数据库中匹配查询(QuerySet)的对象数量。

<11>first():                   返回第一条记录,等价于[:1][0]

<12>last():                   返回最后一条记录,等价于[::1][0]

 <13>exists():               如果QuerySet包含数据,就返回True,否则返回False。

实例:

      1   查询id为2的书籍信息,并只显示书籍名称和出版日期

1
2
>>> Book.objects.filter(id=2).values("title","publication_date")
        [{'title': 'python Gone', 'publication_date': datetime.date(2019, 5, 24)}]

   2   查询所有的出版信息,并按id降序排列,并尝试使用reverse方法进行反向排序。          

1
2
3
4
5
6
7
>>> Publisher.objects.all().order_by("id")
        [<Publisher: 中国机械出版社>, <Publisher: Boo>, <Publisher: 人民出版社>]
>>> Publisher.objects.all().order_by("id").reverse()
        [<Publisher: 人民出版社>, <Publisher: Boo>, <Publisher: 中国机械出版社>]                
 
>>> Publisher.objects.all().order_by("-id")
        [<Publisher: 人民出版社>, <Publisher: Boo>, <Publisher: 中国机械出版社>]

  3    查询出版社所在的城市信息,城市信息不要重复                

1
2
>>> Publisher.objects.all().values("city").distinct()
        [{'city': '北京'}, {'city': 'beijing'}]

  4    查询城市是北京的出版社,尝试使用exclude方法            

1
2
>>> Publisher.objects.all().filter(city='beijing')
        [<Publisher: Boo>]

5    查询男作者的数量            

1
2
>>> AuthorDetail.objects.filter(sex=0).count()
       1

二 多表关联查询

           1   外键关联查询

                  >>> AuthorDetail.objects.all().values("author")

        [{'author': 1}]
>>> AuthorDetail.objects.all().values("author__name")
        [{'author__name': 'alex'}]

2  多对多关联查询

      >>> Book.objects.all().filter(title='python Gone').values("authors")

        [{'authors': 1}, {'authors': 2}]
>>> Book.objects.all().filter(title='python Gone').values("authors__name")
        [{'authors__name': 'alex'}, {'authors__name': 'alvin'}]

>>> Book.objects.all().filter(authors__name='alex').values('title')
        [{'title': 'python Gone'}]

多表查询技巧:

       __:两个下划线可以生成连接查询,查询关联的字段信息

      _set:提供了对象访问相关联表数据的方法。但是这种方法只能是相关类访问定义了关系的类(主键类访问外键类)

三 聚合查询和分组查询

    1  annotate(*args,**kwargs):可以为QuerySet每一个对象添加注解。可以通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),用于分组查询

    2  aggregate(*args,**kwargs):通过对QuerySet进行计算,返回一个聚合值的字典。

aggregate()中每一个参数都指定一个包含在字典中的返回值。用于聚合查询。

聚合函数(Aggregation Functions)

所在位置:django.db.models

1 Avg:  返回所给字段的平均值

2 Count: 根据所给的关联字段返回被关联的model的数量

3 Max:返回所给字段的最大值

4 Sum:计算所给字段的总和

  为了演示实例,在这里我们给Book加一个price字段:

       

  然后同步数据库:makemigrations, migrate

  另外,创建一些新的书:

        

  注意,在pycharm的database下创建新的对象,有个bug:

        

   我现在添入了第六条数据,可是,左上角的row仍然显示5,如果refresh:

       

   点击yes,添加的数据则会消失。

    解决方法:当你创建完一条数据后,点击+按钮,row变成6了,数据自动就提交了,然后再把新创建的空行删除(-)就可以了!

    Book对象添加完,还差一个字段:authors,绑定多对多的关系:

      

    插曲结束,转入正题:

           python manage.py shell(重新打开),因为添加了新的字段

      

           实例:

            1   查询中国邮电大学出版社出版了多少本书?           

       

            2   查询alex出的书总价格                   

       

            3   查询各个作者出的书的总价格

                         这里就涉及到分组了,分组条件是authors__name,

            

            4   查询各个出版社最便宜的书价是多少

       

4.3.4  删除

      >>> Book.objects.filter(id=1).delete()

       (3, {'app01.Book_authors': 2, 'app01.Book': 1})

       我们表面上删除了一条信息,实际却删除了三条,因为我们删除的这本书在Book_authors表中有两条相关信息,这种删除方式就是django默认的级联删除。

posted @ 2017-08-24 14:48  不哭不闹,不吵不笑  阅读(121)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3