昨日内容回顾

1. 为什么要做前后端分离?-前后端交给不同的人来编写,职责划分明确。-API (IOS,安卓,PC,微信小程序...)-vue.js等框架编写前端时,会比之前写jQuery更简单快捷。2. 对于后端人员,主要为前端提供:API(接口)

以前的你的接口:

http://127.0.0.1:8000/index/http://127.0.0.1:8000/users/http://127.0.0.1:8000/add_users/http://127.0.0.1:8000/del_users/http://127.0.0.1:8000/edit_users/restful 规范:

http://127.0.0.1:8000/users/

3. 谈谈你对restful规范的理解?1. 使用https代替http

https://www.luffycity.com/course/detail/web/3http://www.luffycity.com/course/detail/web/3

2. 在URL中体现自己写的是API

https://www.luffycity.com/api/https://api.luffycity.com/可能会跨域3. 在URL中体现版本

https://www.luffycity.com/api/v1/users

https://www.luffycity.com/api/v2/users4. 名词(面向资源编程)

https://www.luffycity.com/api/v1/users

https://www.luffycity.com/api/v1/song5. 行为

https://www.luffycity.com/api/v1/users

method:

get,获取

post,新建

put,更新

patch,局部更新

delete,删除6. 条件

https://www.luffycity.com/api/v1/users?page=1https://www.luffycity.com/api/v1/users?page=1&gender=2

7. 状态码200

301

302

404

500推荐使用code:defxx(request):

ret= {'code':1000,'data':None}try:

...exceptExptions as e:

ret['status'] = 1001ret['error'] = 'xxxx错误'

returnJsonResponse(ret)8. 错误信息

{

code:10001,

error:'用户名或密码错误'}9. 返回结果:

GET:

https://www.luffycity.com/api/v1/users

响应:

{

code:1000,

data: [

{'name':'赵森','age':19},

{'name':'赵云','age':16},

{'name':'赵云','age':16},

{'name':'赵云','age':16},

{'name':'赵云','age':16},

]

}

GET:

https://www.luffycity.com/api/v1/users/1/响应:

{

code:1000,

data:{'name':'赵森','age':19},

}

POST:

https://www.luffycity.com/api/v1/users

请求体:

{'name':'大表哥','age':19}

响应(不要):

{

code:1000,

data:{'id':9, 'name':'大表哥','age':19}

}

PUT/PATCH:

https://www.luffycity.com/api/v1/users

请求体:

{'name':'大表哥','age':19}

响应(不要):

{

code:1000,

data:{'id':9, 'name':'大表哥','age':19}

}

DELETE:

...10. hyper link

访问:https://www.luffycity.com/api/v1/users

{

code:1000,

data:[

{'id':1,'name':'赵森','age':19, 'depart':https://www.luffycity.com/api/v1/depart/1/},

{'id':1,'name':'赵森','age':19, 'depart':https://www.luffycity.com/api/v1/depart/1/},

{'id':1,'name':'赵森','age':19, 'depart':https://www.luffycity.com/api/v1/depart/1/},

{'id':1,'name':'赵森','age':19, 'depart':https://www.luffycity.com/api/v1/depart/1/},

{'id':1,'name':'赵森','age':19, 'depart':https://www.luffycity.com/api/v1/depart/1/},

]

}

https://www.luffycity.com/api/v1/users

{

code:1000,

data:[

{'id':1,'name':'赵森','age':19, 'depart_title':'公关部'},

{'id':1,'name':'赵森','age':19, 'depart_title':'公关部'},

{'id':1,'name':'赵森','age':19, 'depart_title':'公关部'},

{'id':1,'name':'赵森','age':19, 'depart_title':'公关部'},

{'id':1,'name':'赵森','age':19, 'depart_title':'公关部'},

]

}4. django rest framework框架的作用?

帮助开发者可以快速开发出遵循restful规范的API5. django rest framework框架都有哪些组件(10)?

版本

权限

认证

节流

分页

解析器****序列化*****视图****路由

渲染器

View Code

补充:

http传输的数据是明文的,https传输的数据是加密的

简单情况下,使用status,1表示成功,0表示失败

复杂情况下,使用code,查看支付宝api的code说明

一般写接口的时候,推荐使用code。错误信息是服务器返回的!

根据restful规范,POST请求,要返回新增的完整数据。但是,如果前端不需要这些信息,可以不返回!

规定是死的,人是活的。所以要看应用场景!

delete默认是没有返回信息的,但是,还是得返回一个状态。谁知道你,删除有没有成功呢?

hyper link,返回信息中包含超链接。这个也是看需求了

restful规范,不光是python才有。其他语言,也遵循这个规范!

一、路飞学城背景

初期

2012年,有2个人讲python。其中一个是alex,主要是周末班。有7个人,当时没有固定场所,在7个人所在公司的办公室讲课。

一周换一家公司。

2014年,alex进入汽车之家。2014下半年,和佩奇。讲Python,也是讲周末班。有了固定场所--沙河。

线下班,复制难。不好拿投资,规模扩大难。

第一版:给视频+拉群

第一版,在线教育。弄一个QQ群,将视频放到网上。给钱买视频,比如1000块,拉群进去讨论。

学成率比较低,学员自己坚持不下去。10%以下的成功率!

第二版:51cto合作

第二版,和51cto合作,将视频交由51cto管理。每周三,周五做直播,直播2小时。主要做在线答疑!

前期看直播的人,比较多。做了3次直播之后,指数急剧下滑,因为学员技术参差不齐,后面都听不懂了!

最终以失败告终,51cto下架

第三版:51cto合作-->微职位

由alex和51cto谈判,推动51cto的微职位,就是一对一辅导。现今,51cto官网,还保留者微职位。

还是给视频,并且分配一个导师,协助学员学习。并不是真正的一对一,找兼容人员。比如优秀的毕业生,1000~1500的费用。

导师主动跟进学员学习!

好的导师,一个人,可以对接50个人。有些人,遇到问题,会先自己搞定,搞不定,才会找导师。提升自己的解决问题能力,导师就会很轻松。

学成率也不高,相对于以前的看视频,学成率有提升!

第一个问题,导师问题。导师每周跟进一次。有些不问问题的学生,可能忽略跟进了

第二个问题,学生问题。周末在家一天,大多数学不学下的,出去玩,或者打游戏了。学生不积极,没有明确目标

第三个问题,视频问题。针对的学员的状态,是不同的!还有一些,不录制的视频。跟线上不一样!

第四个问题,扩张问题。光靠线下,无法壮大。

第四版:路飞学城

老男孩,自己做路飞学城。初期,由学生投资,占2%,还有销售投资,2个人,一人50万。路飞学城正式成立!

视频,单独录制

由导致单独录制,根据知识点,录制一小段

导师奖惩

以前是靠人工,现在是自动化。

聊天信息,导师上传到服务器。每周都要有,7天聊天没有,就扣钱!

学员购买课程后,安排导师,导师账号有一定的金额。导师没有安装规定,就扣钱。导师自己肯定不愿意扣钱的,所以会主动跟进!

学员不做作业,导师差评,都扣钱

学员换导师,导师分配的金额就没有了,给另外的导师!

以前导师是兼值的,没有办法做到实时回复,导师也有自己公司的事情要做。

后来招了全职导师,做了服务升级。这些是需要掏钱的!

颠覆了在线教育,以前都是看视频,现在是一对一辅导!

学员奖惩

学生,有奖学金

作业做的好,学的快,有奖励金额!

全线课程6个月,是有时效的。过了6个月,就没有了!需要重新购买,这样可以督促学生学习!

学习是根据阶段的,一个阶段必须考核通过,才能进入下一阶段。不能超阶段学习!

每个模块的视频是按照阶段进行的,不是随意看的!

项目架构

主站

使用vue.js + django rest framework的架构,前后端分离。全面采用https,前端和后端在同一台服务器(同域名同端口),避免跨域问题。

主站,注意是给学员使用的,用来购买课程,查看视频等....

里面没有退款按钮,但是有签订协议的。可以休学,延长时间。

退款,需要联系销售人员确认,跟进。为什么不能做到退款瞬时完成呢?因为有竞争对手,它会刷量。

账户的流入流出,是有手续费的。量大的话,会亏损的。

淘宝目前是7天退款,也是为了避免刷量。

导师后台

主要给导师使用的,账户金额,学员跟进...

管理后台

主要是运营使用,用来上传视频,编辑课程相关信息...

开发人员

主站:-前端:- 前端姑娘 v1,vue.js 1.0

- 前端姑娘 v2,vue.js 2.0(目前全面使用2.0)-后端:-老村长-产品经理- alex/wu sir/文周

导师: 1人

管理后台:1人+ 兼职导师

View Code

二、django ContentType组件

请求头ContentType

ContentType指的是请求体的编码类型,常见的类型共有3种:

1. application/x-www-form-urlencoded

这应该是最常见的 POST 提交数据的方式了。浏览器的原生

表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。请求类似于下面这样(无关的请求头在本文中都省略掉了)

POST http://www.example.com HTTP/1.1Content-Type: application/x-www-form-urlencoded;charset=utf-8user=yuan&age=22

View Code

2. multipart/form-data

这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让

表单的 enctype 等于 multipart/form-data。直接来看一个请求示例:

POST http://www.example.com HTTP/1.1Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA------WebKitFormBoundaryrGKCBY7qhFd3TrwA

Content-Disposition: form-data; name="user"yuan------WebKitFormBoundaryrGKCBY7qhFd3TrwA

Content-Disposition: form-data; name="file"; filename="chrome.png"Content-Type: image/png

PNG ... content of chrome.png ...------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

View Code

这个例子稍微复杂点。首先生成了一个 boundary 用于分割不同的字段,为了避免与正文内容重复,boundary 很长很复杂。然后 Content-Type 里指明了数据是以 multipart/form-data 来编码,本次请求的 boundary 是什么内容。消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary 开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 --boundary-- 标示结束。关于 multipart/form-data 的详细定义,请前往 rfc1867 查看。

这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持。

上面提到的这两种 POST 数据的方式,都是浏览器原生支持的,而且现阶段标准中原生

表单也只支持这两种方式(通过 元素的 enctype 属性指定,默认为 application/x-www-form-urlencoded。其实 enctype 还支持 text/plain,不过用得非常少)。

随着越来越多的 Web 站点,尤其是 WebApp,全部使用 Ajax 进行数据交互之后,我们完全可以定义新的数据提交方式,给开发带来更多便利。

3. application/json

application/json 这个 Content-Type 作为响应头大家肯定不陌生。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。

JSON 格式支持比键值对复杂得多的结构化数据,这一点也很有用。记得我几年前做一个项目时,需要提交的数据层次非常深,我就是把数据 JSON 序列化之后来提交的。不过当时我是把 JSON 字符串作为 val,仍然放在键值对里,以 x-www-form-urlencoded 方式提交。

举例:

新建一个项目untitled1

修改urls.py

from django.contrib importadminfrom django.urls importpathfrom app01 importviews

urlpatterns=[

path('admin/', admin.site.urls),

path('index/', views.index),

]

View Code

修改views.py

from django.shortcuts importrender,HttpResponsefrom django.views.decorators.csrf importcsrf_exempt,csrf_protect#Create your views here.

@csrf_exempt #跳过csrf

defindex(request):print(request.POST)return HttpResponse('...')

View Code

启动项目,访问首页

这个时候,boss,发了一个post请求,给index

新建一个boss.py,放在项目根目录中

importrequests

requests.post(

url='http://127.0.0.1:8000/index/',

data={"user":"root","pwd":123}

)

View Code

执行boss.py,查看Pycharm控制台输出:

收到了一条post请求

假设boss发送的是json数据呢?

再次执行boss.py,查看控制台

发现得到是空字典?为什么呢?boss说,已经给你发过去了,你怎么回答?

注意:request.POST不是万能的,因为它只接收请求头为application/x-www-form-urlencoded,其他类型一概不支持。

查看源码,只有为application/x-www-form-urlencoded,才会解析数据

def_load_post_and_files(self):"""Populate self._post and self._files if the content-type is a form type"""

if self.method != 'POST':

self._post, self._files= QueryDict(encoding=self._encoding), MultiValueDict()return

if self._read_started and not hasattr(self, '_body'):

self._mark_post_parse_error()return

if self.content_type == 'multipart/form-data':if hasattr(self, '_body'):#Use already read data

data =BytesIO(self._body)else:

data=selftry:

self._post, self._files=self.parse_file_upload(self.META, data)exceptMultiPartParserError:#An error occurred while parsing POST data. Since when

#formatting the error the request handler might access

#self.POST, set self._post and self._file to prevent

#attempts to parse POST data again.

#Mark that an error occurred. This allows self.__repr__ to

#be explicit about it instead of simply representing an

#empty POST

self._mark_post_parse_error()raise

elif self.content_type == 'application/x-www-form-urlencoded':

self._post, self._files= QueryDict(self.body, encoding=self._encoding), MultiValueDict()else:

self._post, self._files= QueryDict(encoding=self._encoding), MultiValueDict()

View Code

使用request.body,它是万能。它直接接收请求体的原始数据,是一个bytes类型的数据

修改views.py,改为body

from django.shortcuts importrender,HttpResponsefrom django.views.decorators.csrf importcsrf_exempt,csrf_protect#Create your views here.

@csrf_exempt #跳过csrf

defindex(request):print(request.body)return HttpResponse('...')

View Code

再次执行脚本,查看控制台输出:

b'{"pwd": 123, "user": "root"}'

如果需要获取user,需要对数据进行解码

from django.shortcuts importrender,HttpResponsefrom django.views.decorators.csrf importcsrf_exempt,csrf_protectimportjson#Create your views here.

@csrf_exempt #跳过csrf

defindex(request):print(request.body)

data= (request.body).decode('utf-8') #解码

dict_data = json.loads(data) #反序列化

print(dict_data,type(dict_data)) #查看类型为dict

print(dict_data.get("user")) #获取user

return HttpResponse('...')

View Code

再次执行脚本,查看控制台输出:

b'{"user": "root", "pwd": 123}'{'pwd': 123, 'user': 'root'} root

ContentType组件

contenttypes 是Django内置的一个应用,可以追踪项目中所有app和model的对应关系,并记录在ContentType表中。

ContentTypes做了什么?

当使用django-admin初始化一个django项目的时候,可以看到在默认的INSTALL_APPS已经包含了django.contrib.contenttypes:

INSTALLED_APPS =['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles',

]

而且注意django.contrib.contenttypes是在django.contrib.auth之后,这是因为auth中的permission系统是根据contenttypes来实现的。

接着查阅了一下django.contrib.contenttypes.models文件:

classContentType(models.Model):

app_label= models.CharField(max_length=100)

model= models.CharField(_('python model class name'), max_length=100)

objects=ContentTypeManager()classMeta:

verbose_name= _('content type')

verbose_name_plural= _('content types')

db_table= 'django_content_type'unique_together= (('app_label', 'model'),)def __str__(self):return self.name

View Code

可以看到ContentType就是一个简单的django model,而且它在数据库中的表的名字为django_content_type。

在第一次对Django的model进行migrate之后,就可以发现在数据库中出现了一张默认生成的名为django_content_type的表。

如果没有建立任何的model,默认django_content_type是这样的:

+----+--------------+--------------+

| id | app_label | model |

+----+--------------+--------------+

| 1 | admin | logentry |

| 2 | auth | group |

| 3 | auth | user |

| 4 | auth | permission |

| 5 | contenttypes | contenttype |

| 6 | sessions | session |

+----+--------------+--------------+

View Code

因此,django_content_type记录了当前的Django项目中所有model所属的app(即app_label属性)以及model的名字(即model属性)。

当然,django_content_type并不只是记录属性这么简单,contenttypes是对model的一次封装,因此可以通过contenttypes动态的访问model类型,而不需要每次import具体的model类型

每当我们创建了新的model并执行数据库迁移后,ContentType表中就会自动新增一条记录。比如我在应用app01的models.py中创建表class Electrics(models.Model): pass。从数据库查看ContentType表,显示如下:

idapp_labelmodel

admin, auth等内置应用…

5

contenttypes

contenttype

6

app01

electrics

举例说明

方式1:适用于1张表和另一张表要关联的时候。

那么这个表有什么作用呢?这里提供一个场景,路飞学城

1.路飞学城表设计

2.将2个价格策略表合并1张表。

3.如果再加一张表,那价格策略表的表结构会发生改变。 这样不合理的,我们的表结构一般设计完就不会改变。

方式2:适用于1张表和多张表关联的时候。

4.接下来换一种方式。表名+id 数据库表结构不会改变。

5.创建一个新项目

6.创建表

修改models.py

from django.db importmodels#Create your models here.

classCourse(models.Model):"""普通课程"""title= models.CharField(max_length=32)classDegreeCourse(models.Model):"""学位课程"""title= models.CharField(max_length=32)classPricePolicy(models.Model):"""价格策略"""price=models.IntegerField()

period=models.IntegerField()

table_name= models.CharField(verbose_name="关联的表名称")

object_id= models.CharField(verbose_name="关联的表中的数据行ID")

View Code

方式3:ContentType组件

7.settings.py,默认就有contenttypes

8. 使用ContentType组件

修改models.py

主要改动部分如下:

from django.contrib.contenttypes.fields importGenericForeignKey,GenericRelationfrom django.contrib.contenttypes.models importContentType

content_type= models.ForeignKey(ContentType, verbose_name="关联的表名称",on_delete=models.CASCADE)

object_id= models.IntegerField(verbose_name="关联的表中的数据行ID")

View Code

完整代码如下:

from django.db importmodelsfrom django.contrib.contenttypes.fields importGenericForeignKey,GenericRelationfrom django.contrib.contenttypes.models importContentType#Create your models here.

classCourse(models.Model):"""普通课程"""title= models.CharField(max_length=32)classDegreeCourse(models.Model):"""学位课程"""title= models.CharField(max_length=32)classPricePolicy(models.Model):"""价格策略"""price=models.IntegerField()

period=models.IntegerField()

content_type= models.ForeignKey(ContentType, verbose_name="关联的表名称",on_delete=models.CASCADE)

object_id= models.IntegerField(verbose_name="关联的表中的数据行ID")

View Code

9.假设,表数据很多,有个关联表的名字改了,需要改所有的数据很麻烦。那就再创建一张表,专门存放表名字。

10. 第三张表不用自己创建 ContentType 组件已经帮我们创建好了,专门用来存放表名字。

11.生成表,查看表数据

使用2个命令生成表

python manage.py makemigrations

python manage.py migrate

效果如下:

注意:价格策略表,它是万能的。可以适用于任何课程的价格策略。

contenttypes 应用

这里提供一个场景,网上商城购物时,会有各种各样的优惠券,比如通用优惠券,满减券,或者是仅限特定品类的优惠券。在数据库中,可以通过外键将优惠券和不同品类的商品表关联起来:

from django.db importmodelsfrom django.contrib.contenttypes.fields importGenericForeignKey,GenericRelationfrom django.contrib.contenttypes.models importContentType#Create your models here.

classElectrics(models.Model):"""id name

1 日立冰箱

2 三星电视

3 小天鹅洗衣机"""name= models.CharField(max_length=32)classFoods(models.Model):"""id name

1 面包

2 烤鸭"""name= models.CharField(max_length=32)classClothes(models.Model):

name= models.CharField(max_length=32)classCoupon(models.Model):"""id name Electrics Foods Clothes more...

1 通用优惠券 null null null

2 冰箱满减券 2 null null

3 面包狂欢节 null 1 null"""name= models.CharField(max_length=32)

electric_obj= models.ForeignKey(to='Electrics', null=True,on_delete=models.CASCADE)

food_obj= models.ForeignKey(to='Foods', null=True,on_delete=models.CASCADE)

cloth_obj= models.ForeignKey(to='Clothes', null=True,on_delete=models.CASCADE)

View Code

如果是通用优惠券,那么所有的ForeignKey为null,如果仅限某些商品,那么对应商品ForeignKey记录该商品的id,不相关的记录为null。但是这样做是有问题的:实际中商品品类繁多,而且很可能还会持续增加,那么优惠券表中的外键将越来越多,但是每条记录仅使用其中的一个或某几个外键字段。

通过使用contenttypes 应用中提供的特殊字段GenericForeignKey,我们可以很好的解决这个问题。只需要以下三步:

在model中定义ForeignKey字段,并关联到ContentType表。通常这个字段命名为“content_type”

在model中定义PositiveIntegerField字段,用来存储关联表中的主键。通常这个字段命名为“object_id”

在model中定义GenericForeignKey字段,传入上述两个字段的名字。

为了更方便查询商品的优惠券,我们还可以在商品类中通过GenericRelation字段定义反向关系。

创建一个新的项目tmall

修改models.py

from django.db importmodelsfrom django.contrib.contenttypes.models importContentTypefrom django.contrib.contenttypes.fields importGenericForeignKey,GenericRelationclassElectrics(models.Model):

name= models.CharField(max_length=32)

coupons= GenericRelation(to='Coupon') #用于反向查询,不会生成表字段

def __str__(self):returnself.nameclassFoods(models.Model):

name= models.CharField(max_length=32)

coupons= GenericRelation(to='Coupon')def __str__(self):returnself.nameclassClothes(models.Model):

name= models.CharField(max_length=32)

coupons= GenericRelation(to='Coupon')def __str__(self):returnself.nameclassCoupon(models.Model):

name= models.CharField(max_length=32)

content_type= models.ForeignKey(ContentType,on_delete=models.CASCADE,default=None) #step 1

object_id = models.PositiveIntegerField(default=None) #step 2

content_object = GenericForeignKey('content_type', 'object_id') #step 3

def __str__(self):returnself.nameclassOftenAskedQuestion(models.Model):"""常见问题"""content_type= models.ForeignKey(ContentType,on_delete=models.CASCADE,default=None)

object_id= models.PositiveIntegerField(default=None)

content_object= GenericForeignKey('content_type', 'object_id')

question= models.CharField(max_length=255)

answer= models.TextField(max_length=1024)

View Code

使用2个命令,生成表

python manage.py makemigrations

python manage.py migrate

创建记录和查询

使用navicat打开sqllite数据库,执行以下sq

INSERT INTO app01_coupon ("id", "name", "content_type_id", "object_id") VALUES (1, '通用优惠券', 7, 2);

INSERT INTO app01_coupon ("id", "name", "content_type_id", "object_id") VALUES (2, '冰箱满减券', 7, 1);

INSERT INTO app01_coupon ("id", "name", "content_type_id", "object_id") VALUES (3, '面包狂欢节', 8, 1);

INSERT INTO app01_electrics ("id", "name") VALUES (1, '日立冰箱');

INSERT INTO app01_electrics ("id", "name") VALUES (2, '三星电视');

INSERT INTO app01_electrics ("id", "name") VALUES (3, '小天鹅洗衣机');

INSERT INTO app01_foods ("id", "name") VALUES (1, '面包');

INSERT INTO app01_foods ("id", "name") VALUES (2, '烤鸭');

INSERT INTO app01_oftenaskedquestion ("id", "object_id", "question", "answer", "content_type_id") VALUES (1, 1, '多少钱', 10, 7);

View Code

解释一下,electrics和oftenaskedquestion是如何关联的

先来看django_content_type表记录,electrics表对应的主键id为7

再看看oftenaskedquestion表记录

object_id表示electrics表的主键id=1的记录

question,表示问题

answer,表示答案

content_type_id,表示electrics表在django_content_type表中的主键id

通过最后一条sql语句,electrics表和oftenaskedquestion表,就建立了一条关联记录!

修改urls.py

from django.contrib importadminfrom django.urls importpathfrom app01 importviews

urlpatterns=[

path('admin/', admin.site.urls),

path('test/', views.test),

]

View Code

修改views.py

from django.shortcuts importrender, HttpResponsefrom app01 importmodelsfrom django.contrib.contenttypes.models importContentTypedeftest(request):if request.method == 'GET':#ContentType表对象有model_class() 方法,取到对应model

content = ContentType.objects.filter(app_label='app01', model='electrics').first() #表名小写

cloth_class = content.model_class() #cloth_class 就相当于models.Electrics

res =cloth_class.objects.all()print(res)#为三星电视(id=2)创建一条优惠记录

s_tv = models.Electrics.objects.filter(id=2).first()print(s_tv)#models.Coupon.objects.create(name='电视优惠券', content_object=s_tv)

#查询优惠券(id=1)绑定了哪些商品

coupon_obj = models.Coupon.objects.filter(id=1).first()

prod=coupon_obj.content_objectprint(prod)# #查询三星电视(id=2)的所有优惠券

res =s_tv.coupons.all()print(res)# #查询obj的所有优惠券:如果没有定义反向查询字段,通过如下方式:

content = ContentType.objects.filter(app_label='app01', model='electrics').first()print(content)

res= models.OftenAskedQuestion.objects.filter(content_type=content, object_id=coupon_obj.pk).all()print(res)return HttpResponse('....')

View Code

启动项目,访问页面

查看Pycharm控制台输出:

, , ]>三星电视

三星电视]>electrics]>

总结:

当一张表和多个表FK关联,并且多个FK中只能选择其中一个或其中n个时,可以利用contenttypes app,只需定义三个字段就搞定!

一、路飞学城表结构

创建项目luffycity,应用名为api。注意:django版本为1.11

修改models.py

from django.contrib.contenttypes.fields importGenericForeignKey, GenericRelationfrom django.contrib.contenttypes.models importContentTypefrom django.db.models importQfrom django.utils.safestring importmark_safefrom django.db importmodelsimporthashlib######################### 课程相关 ########################

classCourseCategory(models.Model):"""课程大类, e.g 前端 后端..."""name= models.CharField(max_length=64, unique=True)def __str__(self):return "%s" %self.nameclassMeta:

verbose_name_plural= "01.课程大类"

classCourseSubCategory(models.Model):"""课程子类, e.g python linux"""category= models.ForeignKey("CourseCategory")

name= models.CharField(max_length=64, unique=True)def __str__(self):return "%s" %self.nameclassMeta:

verbose_name_plural= "02.课程子类"

classDegreeCourse(models.Model):"""学位课程"""name= models.CharField(max_length=128, unique=True)

course_img= models.CharField(max_length=255, verbose_name="缩略图")

brief= models.TextField(verbose_name="学位课程简介", )

total_scholarship= models.PositiveIntegerField(verbose_name="总奖学金(贝里)", default=40000) #2000 2000

mentor_compensation_bonus = models.PositiveIntegerField(verbose_name="本课程的导师辅导费用(贝里)", default=15000)

period= models.PositiveIntegerField(verbose_name="建议学习周期(days)", default=150) #为了计算学位奖学金

prerequisite = models.TextField(verbose_name="课程先修要求", max_length=1024)

teachers= models.ManyToManyField("Teacher", verbose_name="课程讲师")#用于GenericForeignKey反向查询, 不会生成表字段,切勿删除

#coupon = GenericRelation("Coupon")

#用于GenericForeignKey反向查询,不会生成表字段,切勿删除

degreecourse_price_policy = GenericRelation("PricePolicy")def __str__(self):returnself.nameclassMeta:

verbose_name_plural= "03.学位课"

classTeacher(models.Model):"""讲师、导师表"""name= models.CharField(max_length=32)

role_choices= ((0, '讲师'), (1, '导师'))

role= models.SmallIntegerField(choices=role_choices, default=0)

title= models.CharField(max_length=64, verbose_name="职位、职称")

signature= models.CharField(max_length=255, help_text="导师签名", blank=True, null=True)

image= models.CharField(max_length=128)

brief= models.TextField(max_length=1024)def __str__(self):returnself.nameclassMeta:

verbose_name_plural= "04.导师或讲师"

classScholarship(models.Model):"""学位课程奖学金"""degree_course= models.ForeignKey("DegreeCourse")

time_percent= models.PositiveSmallIntegerField(verbose_name="奖励档位(时间百分比)", help_text="只填百分值,如80,代表80%")

value= models.PositiveIntegerField(verbose_name="奖学金数额")def __str__(self):return "%s:%s" %(self.degree_course, self.value)classMeta:

verbose_name_plural= "05.学位课奖学金"

classCourse(models.Model):"""专题课/学位课模块表"""name= models.CharField(max_length=128, unique=True)

course_img= models.CharField(max_length=255)

sub_category= models.ForeignKey("CourseSubCategory")

course_type_choices= ((0, '付费'), (1, 'VIP专享'), (2, '学位课程'))

course_type= models.SmallIntegerField(choices=course_type_choices)#不为空;学位课的某个模块

#为空;专题课

degree_course = models.ForeignKey("DegreeCourse", blank=True, null=True, help_text="若是学位课程,此处关联学位表")

brief= models.TextField(verbose_name="课程概述", max_length=2048)

level_choices= ((0, '初级'), (1, '中级'), (2, '高级'))

level= models.SmallIntegerField(choices=level_choices, default=1)

pub_date= models.DateField(verbose_name="发布日期", blank=True, null=True)

period= models.PositiveIntegerField(verbose_name="建议学习周期(days)", default=7) # order = models.IntegerField("课程顺序", help_text="从上一个课程数字往后排")

attachment_path= models.CharField(max_length=128, verbose_name="课件路径", blank=True, null=True)

status_choices= ((0, '上线'), (1, '下线'), (2, '预上线'))

status= models.SmallIntegerField(choices=status_choices, default=0)

template_id= models.SmallIntegerField("前端模板id", default=1)#coupon = GenericRelation("Coupon")

#用于GenericForeignKey反向查询,不会生成表字段,切勿删除

price_policy = GenericRelation("PricePolicy")

asked_question= GenericRelation("OftenAskedQuestion")def __str__(self):return "%s(%s)" %(self.name, self.get_course_type_display())def save(self, *args, **kwargs):if self.course_type == 2:if notself.degree_course:raise ValueError("学位课程必须关联对应的学位表")

super(Course, self).save(*args, **kwargs)classMeta:

verbose_name_plural= "06.专题课或学位课模块"

classCourseDetail(models.Model):"""课程详情页内容"""course= models.OneToOneField("Course")

hours= models.IntegerField("课时")

course_slogan= models.CharField(max_length=125, blank=True, null=True)

video_brief_link= models.CharField(verbose_name='课程介绍', max_length=255, blank=True, null=True)

why_study= models.TextField(verbose_name="为什么学习这门课程")

what_to_study_brief= models.TextField(verbose_name="我将学到哪些内容")

career_improvement= models.TextField(verbose_name="此项目如何有助于我的职业生涯")

prerequisite= models.TextField(verbose_name="课程先修要求", max_length=1024)

recommend_courses= models.ManyToManyField("Course", related_name="recommend_by", blank=True)

teachers= models.ManyToManyField("Teacher", verbose_name="课程讲师")def __str__(self):return "%s" %self.courseclassMeta:

verbose_name_plural= "07.课程或学位模块详细"

classOftenAskedQuestion(models.Model):"""常见问题"""content_type= models.ForeignKey(ContentType) #关联course or degree_course

object_id =models.PositiveIntegerField()

content_object= GenericForeignKey('content_type', 'object_id')

question= models.CharField(max_length=255)

answer= models.TextField(max_length=1024)def __str__(self):return "%s-%s" %(self.content_object, self.question)classMeta:

unique_together= ('content_type', 'object_id', 'question')

verbose_name_plural= "08. 常见问题"

classCourseOutline(models.Model):"""课程大纲"""course_detail= models.ForeignKey("CourseDetail")

title= models.CharField(max_length=128)#前端显示顺序

order = models.PositiveSmallIntegerField(default=1)

content= models.TextField("内容", max_length=2048)def __str__(self):return "%s" %self.titleclassMeta:

unique_together= ('course_detail', 'title')

verbose_name_plural= "09. 课程大纲"

classCourseChapter(models.Model):"""课程章节"""course= models.ForeignKey("Course", related_name='coursechapters')

chapter= models.SmallIntegerField(verbose_name="第几章", default=1)

name= models.CharField(max_length=128)

summary= models.TextField(verbose_name="章节介绍", blank=True, null=True)

pub_date= models.DateField(verbose_name="发布日期", auto_now_add=True)classMeta:

unique_together= ("course", 'chapter')

verbose_name_plural= "10. 课程章节"

def __str__(self):return "%s:(第%s章)%s" %(self.course, self.chapter, self.name)classCourseSection(models.Model):"""课时目录"""chapter= models.ForeignKey("CourseChapter", related_name='coursesections')

name= models.CharField(max_length=128)

order= models.PositiveSmallIntegerField(verbose_name="课时排序", help_text="建议每个课时之间空1至2个值,以备后续插入课时")

section_type_choices= ((0, '文档'), (1, '练习'), (2, '视频'))

section_type= models.SmallIntegerField(default=2, choices=section_type_choices)

section_link= models.CharField(max_length=255, blank=True, null=True, help_text="若是video,填vid,若是文档,填link")

video_time= models.CharField(verbose_name="视频时长", blank=True, null=True, max_length=32) #仅在前端展示使用

pub_date = models.DateTimeField(verbose_name="发布时间", auto_now_add=True)

free_trail= models.BooleanField("是否可试看", default=False)classMeta:

unique_together= ('chapter', 'section_link')

verbose_name_plural= "11. 课时"

def __str__(self):return "%s-%s" %(self.chapter, self.name)classHomework(models.Model):

chapter= models.ForeignKey("CourseChapter")

title= models.CharField(max_length=128, verbose_name="作业题目")

order= models.PositiveSmallIntegerField("作业顺序", help_text="同一课程的每个作业之前的order值间隔1-2个数")

homework_type_choices= ((0, '作业'), (1, '模块通关考核'))

homework_type= models.SmallIntegerField(choices=homework_type_choices, default=0)

requirement= models.TextField(max_length=1024, verbose_name="作业需求")

threshold= models.TextField(max_length=1024, verbose_name="踩分点")

recommend_period= models.PositiveSmallIntegerField("推荐完成周期(天)", default=7)

scholarship_value= models.PositiveSmallIntegerField("为该作业分配的奖学金(贝里)")

note= models.TextField(blank=True, null=True)

enabled= models.BooleanField(default=True, help_text="本作业如果后期不需要了,不想让学员看到,可以设置为False")classMeta:

unique_together= ("chapter", "title")

verbose_name_plural= "12. 章节作业"

def __str__(self):return "%s - %s" %(self.chapter, self.title)#class CourseReview(models.Model):#"""课程评价"""#enrolled_course = models.OneToOneField("EnrolledCourse")#about_teacher = models.FloatField(default=0, verbose_name="讲师讲解是否清晰")#about_video = models.FloatField(default=0, verbose_name="内容实用")#about_course = models.FloatField(default=0, verbose_name="课程内容通俗易懂")#review = models.TextField(max_length=1024, verbose_name="评价")#disagree_number = models.IntegerField(default=0, verbose_name="踩")#agree_number = models.IntegerField(default=0, verbose_name="赞同数")#tags = models.ManyToManyField("Tags", blank=True, verbose_name="标签")#date = models.DateTimeField(auto_now_add=True, verbose_name="评价日期")#is_recommend = models.BooleanField("热评推荐", default=False)#hide = models.BooleanField("不在前端页面显示此条评价", default=False)#

#def __str__(self):#return "%s-%s" % (self.enrolled_course.course, self.review)#

#class Meta:#verbose_name_plural = "13. 课程评价(购买课程后才能评价)"#

#

#class DegreeCourseReview(models.Model):#"""学位课程评价#为了以后可以定制单独的评价内容,所以不与普通课程的评价混在一起,单独建表#"""#enrolled_course = models.ForeignKey("EnrolledDegreeCourse")#course = models.ForeignKey("Course", verbose_name="评价学位模块", blank=True, null=True,#help_text="不填写即代表评价整个学位课程", limit_choices_to={'course_type': 2})#about_teacher = models.FloatField(default=0, verbose_name="讲师讲解是否清晰")#about_video = models.FloatField(default=0, verbose_name="视频质量")#about_course = models.FloatField(default=0, verbose_name="课程")#review = models.TextField(max_length=1024, verbose_name="评价")#disagree_number = models.IntegerField(default=0, verbose_name="踩")#agree_number = models.IntegerField(default=0, verbose_name="赞同数")#tags = models.ManyToManyField("Tags", blank=True, verbose_name="标签")#date = models.DateTimeField(auto_now_add=True, verbose_name="评价日期")#is_recommend = models.BooleanField("热评推荐", default=False)#hide = models.BooleanField("不在前端页面显示此条评价", default=False)#

#def __str__(self):#return "%s-%s" % (self.enrolled_course, self.review)#

#class Meta:#verbose_name_plural = "14. 学位课评价(购买课程后才能评价)"

classPricePolicy(models.Model):"""价格与有课程效期表"""content_type= models.ForeignKey(ContentType) #关联course or degree_course

object_id =models.PositiveIntegerField()

content_object= GenericForeignKey('content_type', 'object_id')#course = models.ForeignKey("Course")

valid_period_choices = ((1, '1天'), (3, '3天'),

(7, '1周'), (14, '2周'),

(30, '1个月'),

(60, '2个月'),

(90, '3个月'),

(180, '6个月'), (210, '12个月'),

(540, '18个月'), (720, '24个月'),

)

valid_period= models.SmallIntegerField(choices=valid_period_choices)

price=models.FloatField()classMeta:

unique_together= ("content_type", 'object_id', "valid_period")

verbose_name_plural= "15. 价格策略"

def __str__(self):return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price)

View Code

关键字解释:

Course表

Course表里面有price_policy属性,里面定义了GenericRelation,它是用来做反向查询的

表示它会从django_content_type表中找到model字段为pricepolicy(价格策略表)

PricePolicy表

content_type,约定俗成叫这个命令,它和django_content_type表做了外键关联。ContentType表示django_content_type表

object_id 表示关联表中的主键。通常这个字段命名为“object_id”。

content_object 传入上述两个字段的名字,用来建立外键关系。

Course表,假设id=1,课程名为Python全栈。要加一条价格策略,比如7天30块

查看django_content_type表,course的id为8

那么api_pricepolicy表的记录应该这么写

object_id,表示关联表的主键id,也就是course表的id=1的记录

vaild_period,表示有效期

price,表示价格

content_type_id,表示django_content_type表中主键id。8就表示course表

通过这样,2个表,就建立了一条关系!

django_content_type对所有表做了外键,所以它记录了所有表名!

假设,需要查询Course表中id=1的课程所有价格策略,可以这么查询

obj = models.Course.objects.filter(id=1).first()print(i.price_policy.all())

all()表示查询所有记录

假设,查询所有课程的价格策略

obj =models.Course.objects.all()for i inobj:print(i.price_policy.all())

显示课程列表,并打印每个课程的所有价格信息

obj =models.Course.objects.all()for i inobj:print(i.name)

ps=i.price_policy.all()for p inps:print('-->',p.valid_period,p.price)

执行之后,效果应该是这样的

Python开发入门7天特训营--> 7 10.0

--> 30 50.0Linux系统基础5周入门精讲--> 14 20.0

--> 60 80.0

Course表里面有专题课和学位课。

专题课,可以单独售卖。

Course里面的degree_course。为空,表示专题课。否则为学位课!

判断为空,使用如下语句

models.Course.objects.filter(degree_course__isnull=True)

表结构图关系图如下:

总结:

1. django contenttypes组件的作用?-数据库迁移时,找到所有app中的表并录入到django_content_type表-内置两个字段:

GenericRelation

GenericForeignKey2. 使用contenttypes组件3. 路飞表结构

a. 课程分类-课程大类-课程子类

b. 学位课-学位课-奖学金-老师

c. 专题课or学位课模块- 专题课 or学位课模块-课程详细-课程大纲-常见问题-章节-课时-作业

d. 价格- 价格策略

View Code

今日作业:

一、准备工作:1. 通过admin对13张表录入数据2. ORM练习

a. 查看所有学位课并打印学位课名称以及授课老师

b. 查看所有学位课并打印学位课名称以及学位课的奖学金

c. 展示所有的专题课

models.Course.objects.filter(degree_course__isnull=True)

d. 查看id=1的学位课对应的所有模块名称

e. 获取id=1的专题课,并打印:课程名、级别(中文)、why_study、what_to_study_brief、所有recommend_courses

f. 获取id=1的专题课,并打印该课程相关的所有常见问题

g. 获取id=1的专题课,并打印该课程相关的课程大纲

h. 获取id=1的专题课,并打印该课程相关的所有章节

i. 获取id=1的专题课,并打印该课程相关的所有课时

第1章·Python 介绍、基础语法、流程控制01-课程介绍(一)01-课程介绍(一)01-课程介绍(一)01-课程介绍(一)01-课程介绍(一)

第1章·Python 介绍、基础语法、流程控制01-课程介绍(一)01-课程介绍(一)01-课程介绍(一)01-课程介绍(一)01-课程介绍(一)

i. 获取id=1的专题课,并打印该课程相关的所有的价格策略

二、基于django rest framework 写路飞的接口(作业一+rest framework 序列化)-课程列表API- 课程详细API

View Code

答案

一、ORM查询

a. 查看所有学位课并打印学位课名称以及授课老师

obj= models.DegreeCourse.objects.values("name","teachers__name")print(obj)

b. 查看所有学位课并打印学位课名称以及学位课的奖学金

obj= models.DegreeCourse.objects.all().values("name","total_scholarship")print(obj)

c. 展示所有的专题课

obj= models.Course.objects.filter(degree_course__isnull=True)

d. 查看id=1的学位课对应的所有模块名称

obj= models.Course.objects.filter(id=1).values("name","degree_course")

e. 获取id=1的专题课,并打印:课程名、级别(中文)、why_study、what_to_study_brief、所有recommend_courses

obj= models.Course.objects.filter(degree_course__isnull=True,id=1).values("name","level","coursedetail__why_study","coursedetail__what_to_study_brief","coursedetail__recommend_courses")

f. 获取id=1的专题课,并打印该课程相关的所有常见问题

obj= models.Course.objects.filter(id=1)for i inobj:print(i.asked_question.all())

g. 获取id=1的专题课,并打印该课程相关的课程大纲

obj= models.CourseDetail.objects.filter(id=1).values("courseoutline__title")print(obj)

h. 获取id=1的专题课,并打印该课程相关的所有章节

obj= models.Course.objects.filter(id=1).values("coursechapters__name")print(obj)

i. 获取id=1的专题课,并打印该课程相关的所有的价格策略

obj= models.Course.objects.filter(id=1).values("coursechapters__name","coursechapters__coursesections__name")print(obj)

View Code

二、api

github下载地址

路飞学城python全栈开发_python 全栈开发,Day98(路飞学城背景,django ContentType组件,表结构讲解)...相关推荐

  1. python必备基础代码-新手上路必学的Python函数基础知识,全在这里了(多段代码举例)...

    原标题:新手上路必学的Python函数基础知识,全在这里了(多段代码举例) 导读:函数是Python中最重要.最基础的代码组织和代码复用方式.根据经验,如果你需要多次重复相同或类似的代码,就非常值得写 ...

  2. python 全栈路线_python全栈要学什么 python全栈学习路线

    IT行业,技术要比学历.年龄.从业经验更为重要,技术水平直接决定就业薪资,想要学好python,首先要先了解精通Python语言基础.Python web开发.Python爬虫.Python数据分析这 ...

  3. python全栈测试开发_python全栈的基础知识

    python全栈是什么意思 即从前端页面的实现,到后台代码的编写,再到数据库的管理,一人可以搞定一个公司网站的所有事情,真正实现全栈开发. 全栈只是个概念,也分很多种类真正的全栈工程师涵盖了web开发 ...

  4. python web 全栈开发_Python全栈开发:web框架

    Web框架本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. 上述通过socket来实现了其本质,而对于真实开发中的python ...

  5. python全栈工程_Python全栈工程师(编码)

    Python 入门基础 补充: 主流3操作大系统 Windows: Winxp   Win7 Win8 Win10 Unix: Solaris(SUN) IOS(Apple移动端) Mac OS Li ...

  6. python datetime需要安装_Python全栈工程师学习笔记 | Django的模型层

    Model模型 模型是你的数据的唯一的.权威的信息源.它包含你所储存数据的必要字段和行为. 通常,每个模型对应数据库中唯一的一张表. 每个模型都是django.db.models.Model的一个Py ...

  7. python 栈实现_Python实现栈

    原博文 2017-07-17 17:28 − 栈的操作 Stack() 创建一个新的空栈 push(item) 添加一个新的元素item到栈顶 pop() 弹出栈顶元素 peek() 返回栈顶元素 i ...

  8. php和python哪个有前途_python、java、php,学哪个更有前景?

    python.java.php,学哪个更有前景? 要想分析这些编程语言哪一个更有发展前景,首先要对这些语言有一个大致的了解. Python目前被广泛应用在Web开发.大数据以及人工智能相关的开发领域, ...

  9. python发送excel文件_Python操作Excel, 开发和调用接口,发送邮件

    接口开发: importflaskimporttoolsimportjson,redisimportrandom server= flask.Flask(__name__)#新建一个服务,把当前这个p ...

最新文章

  1. CSS中连接属性的排序
  2. 两个小模型就能吊打大模型!北大校友、谷歌华人一作「模型集合」,CNN、Transformer都适用!...
  3. 线上办公室 x 音视频会议最佳实践
  4. DRF框架—序列化器中的字段校验规则
  5. 超小型php框架,MiniFramework
  6. Hadoop HDFS概念学习系列之shell命令使用HDFS的一些其他命令(十九)
  7. 电容过大导致电压下降_现场| 典型的断直流电源导致开关误分合案例分析
  8. tensorflow 特征预处理总结
  9. JAVA数据类型和运算符2
  10. itunes store服务中断_从服务器开发底层聊一聊协程的实现原理
  11. 神策分析 2.1 重磅上线!报警诊断、多主体分析、渠道追踪等赋能企业数字化经营...
  12. 数据流-移动超平面(HyperPlane)构造
  13. 聊聊页面中的锚点效果和回到顶部
  14. 2023扬州大学计算机考研信息汇总
  15. Android Retrofit
  16. HBase常用过滤器
  17. vim命令set nu
  18. 学习笔记2018-10-26 读论文A single algorithm to retrieve turbidity from remotely-sensed data in all coastal
  19. 关于显著性检验,你想要的都在这儿了!!(基础篇)
  20. 【沃顿商学院学习笔记】宏观经济学——03国际移民与城市发展Immigration and Cities

热门文章

  1. Python 【生成二维码和条形码】
  2. 面向5G基础设施,英特尔使用最新硅光技术
  3. 在docker中运行BaiduPCS-web
  4. CSS自动换行后缩进
  5. 深度学习的边缘坑——ubuntu 16 开机密码循环输入
  6. 用css3的transition实现过渡动画
  7. 程序员代码记事本:Boostnote for Mac
  8. php开启swoole,swoole如何启动
  9. 【2022】WebGoat的安装与测试
  10. 基于Android实现高德地图校内导航出行app项目演示【项目源码+简要论文说明】分享