django-admin 页面效果,增删改查之[查]
这里涉及到的知识点有两个,
一个是get_field() 这里面写的是一个字符串,我们把得到的字符串放进去它返回的是一个类对象,这个字符串是有前提的,前提是这个字符串它要和我们的字段名长得一样才可以,比如我们的book表格,它里面的title字段,我们的title字段是我们的book表格实例化出来的对象,我们放到这个get_field里面的字符串也要是title,不过是加上引号的''title'',然后它会把这个加上引号的title返回给我们就是类对象,
我们一般在django里面自定义组件的时候会用到这个用法,比如,在django,admin里面我们要自定义页面所展示的字段名称,就会在注册表文件里面用到list_display,然后这个list_display它后面是一个列表,列表里面是一个个的字符串,这个时候这个字符串就是"title",我们得到了这个"title"之后就用它返回给我们类对象,在自定义组件里面就会需要这种用法,
还有一个是verbose_name() 它是放在我们的models.py文件里面的,我们在定义表格的时候,会在字段里面加上限制条件,然后在字段的限制条件里面我们把它加进去,verbose_name(),它是对我们的字段进行描述的,比如我们的book里面的title字段,我们可以写verbose_name="书名",像这样,如果我们不写verbose_name的话,它默认就是我们的title,我们在把字段展示到页面的时候,就可以使用这个参数去替代我们的title英文单词,
还有callable()里面传入一个变量,然后返回一个bool值,判断该变量是否是可调用的,可调用就是函数,否则就不是,
还有getattr()里面传入两个参数,一个是对象,另一个是字符串,我们判断该对象是否有该字符串的方法,返回的也是bool值,
下面我们来实现代码:
from django.db import models # Create your models here. class Book(models.Model): title = models.CharField(max_length=32, verbose_name='书名') # 我们的verbose_name是对我们的字段进行描述的, # 这个描述信息是可加可不加的,如果不加的话就使用我们默认的title,如果加上的就显示我们这个描述信息, # 显示,是在我们的组件里面的内置方法里面显示的, def __str__(self): return self.title
组件核心代码:
1 from django.conf.urls import url 2 from django.shortcuts import HttpResponse, render, redirect 3 4 5 class ModelSubject(object): 6 """ 7 我们在这里模拟admin源码里面的ModelAdmin,但是仅仅是其中的一个方法而已, 8 就是增删改查生成url那一段的方法def get_urls()就是这个方法 9 """ 10 list_display = [] 11 12 def __init__(self, model, site): 13 self.model = model # 当我们生成一个实例化对象的时候需要把model这个参数传进来, 14 # 必须要传,它是位置参数,然后我们所传入的那个model就是我们在models.py里面定义的每一个表名也就是类名 15 self.site = site 16 self.namespace = '{}_{}'.format(self.model._meta.app_label, self.model._meta.model_name) 17 # 我们这里的namespace是因为会频繁使用到所以就把它作为一个内置静态属性写入到这里,其他地方如果要调用它就直接使用self.namespace即可 18 # .format的方法:'{}_{}'.format(a,b) 19 20 def stand_li(self, request): 21 # print(self.model) 22 # 所以我们在这里可以获取到当前的url里面的表名,然后直接使用orm语句即可得到当前表格的所有信息 23 24 # 生成表头数据 25 # ['id','title','price',edit] 26 header_list = [] 27 for field in self.list_display: 28 if callable(field): 29 ret = field(self, is_header=True) 30 header_list.append(ret) 31 else: 32 obj = self.model._meta.get_field(field) # 我们的list_display里面是一个个的字符串, 33 # 把字符串放到get_field里面来可以把我们的字符串转换成类对象, 34 header_list.append(obj.verbose_name) # 我们这里的verbose_name在model里面是内置方法, 35 # 我们的verbose_name本质上是对我们的字段进行描述的,比如我们的book里面的title可以在字段约束里面设 36 # verbose_name='书名',类似于这样,把它变成中文,然后我们在前端HTML模板里面渲染的时候就可以渲染出来中文了, 37 # 而不是使用默认的英文,当然了我们如果不设置verbose_name的值那么就使用默认的title作为字段名传到浏览器 38 39 40 # 生成表单数据列表 41 ret = self.model.objects.all() 42 # print('self.list_display', self.list_display) 43 data_list = [] 44 for obj in ret: # 我们遍历这个queryset集合得到的obj是它的每一个对象 45 temp = [] 46 for field in self.list_display: # 我们遍历list_display得到每一个字符串 47 if callable(field): 48 res = field(self, obj) 49 else: 50 res = getattr(obj, field) # 使用getattr方法去判断该对象是否具有,field属性方法, 51 # getattr里面需要两个参数(类对象,字符串属性方法) 52 temp.append(res) 53 data_list.append(temp) 54 print('data_list', data_list) 55 56 """ 57 我们最终得到的数据类型是如下格式:列表套着列表 58 [ 59 使用orm语句得到的每一个类对象,有几个表格就有几个对象 60 [ 61 [1,'python',<a>编辑</a>], 62 [2,'java',<a>编辑</a>], 63 ] 64 ] 65 """ 66 # 生成表单数据 67 return render(request, 'file/hello.html', locals()) 68 69 def add_view(self, request): 70 return HttpResponse('add') 71 72 def edit_view(self, request, id): 73 return HttpResponse('edit') 74 75 def dele_view(self, request, id): 76 return HttpResponse('dele') 77 78 def get_urls(self): 79 temp = [] 80 temp.append(url(r'^$', self.stand_li, name='%s_standlist' % self.namespace)) 81 temp.append(url(r'^(\d+)/dele/', self.dele_view, name='%s_dele' % self.namespace)) 82 temp.append(url(r'^(\d+)/edit/', self.edit_view, name='%s_edit' % self.namespace)) 83 temp.append(url(r'^add/', self.add_view, name='%s_add' % self.namespace)) 84 return temp 85 86 @property 87 def urls(self): 88 return self.get_urls() 89 90 91 class Stark(object): 92 """ 93 我们这里面的功能是可以跟上面的类写到一起去的,但是我们为了功能解耦,所以就分开写了,这里的主要功能就是 94 生成registry的字典,把键值对生成,然后我们最终的结果是要得到一个实例化对象,供我们后面的程序调用,这里的类才是主要的,核心的代码块 95 而我们上面的那个ModelSubject是辅助我们这里的功能,它之所以分发出去是为了便于扩展其他的功能,我们的自定义样式, 96 还有很多的方法和参数,就像我们的admin里面的ModelAdmin一样,长达1400多行代码,单独把它分离出去便于功能的扩展 97 """ 98 99 def __init__(self): 100 self._registry = {} # 这里是定义一个私有属性,就是为了避免被子类修改 101 102 def register(self, model, model_config=None): # 我们是仿照着admin的源码写的组件,这里的model_config默认值是None, 103 # 我们在传参的时候,如果给它传值,那么就使用我们传入的值替换掉这个None 104 # 它的源码里面有这几个参数,我们也要按照顺序把这几个参数加进来 105 if not model_config: 106 model_config = ModelSubject # 我们这里的model_config我们上面的类ModelSubject实例化出来的对象, 107 # 它是上面的类所实例化出来的对象,这一句写得明明白白的,这大白话再看不懂就真是白学了, 108 self._registry[model] = model_config(model, self) 109 110 def get_urls(self): 111 li = [] 112 for model, model_config in self._registry.items(): # 我们在这里所循环的model_config就是 113 # 我们往上数第四行所实例化出来的那个model_config,它是上面的ModelSubject这个类所实例化出来对象, 114 model_name = model._meta.model_name # 这里的._meta.model_name是获取字符串格式的类名, 115 app_label = model._meta.app_label # 这里的._meta.app_labe获取的是字符串格式的App名,都是为了跟url做匹配, 116 sli = url(r'%s%s/' % (app_label, model_name), (model_config.urls, None, None)) # 我们这里的model_config, 117 # 它后面的.urls是在调用一个私有方法,我们的私有方法就是使用.urls来调用,不用加上括号, 118 # 因为有@property这个装饰器在里面起到的作用,然后我们需要找到model_config这个实例对象是哪个类生成的, 119 # 然后找到该类所拥有的方法,从里面找到urls,届时,那个urls就是我们在这里调用的那个urls了, 120 # 所以关键的点就是我们的model_config,老师讲课的时候一再地强调过这个model_config从何而来,这个是关键, 121 li.append(sli) 122 return li 123 124 # 我们最终的数据结构就是这样的,嵌套多层 125 # [ 126 # url( 127 # r'',( 128 # [ 129 # (url(r'',views.add)), 130 # (url(r'',views.edit)), 131 # ], 132 # none,none) 133 # ) 134 # ] 135 @property 136 def urls(self): 137 return self.get_urls(), None, None 138 139 140 site = Stark()
一个App注册model代码:
1 from file.tool.tag import site, ModelSubject 2 from .models import * 3 from django.utils.safestring import mark_safe 4 from django.shortcuts import redirect 5 from django.urls import reverse 6 7 8 class AuthorConfig(ModelSubject): 9 def edit(self, obj=None, is_header=False): 10 if is_header: 11 return '操作' 12 else: 13 # return mark_safe("<a href='/stark/two/author/%s/edit'>编辑</a>" % obj.pk) 14 return mark_safe("<a href='%s'>编辑</a>" % reverse('%s_edit' % self.namespace, args=(obj.pk,))) 15 16 def dele(self, obj=None, is_header=False): 17 18 if is_header: 19 return '操作' 20 else: 21 # return mark_safe("<a href='/stark/two/author/%s/dele'>删除</a>" % obj.pk) 22 return mark_safe("<a href='%s'>删除</a>" % reverse('%s_dele' % self.namespace, args=(obj.pk,))) 23 24 list_display = ['id', 'name', 'age', edit, dele] 25 26 27 site.register(Author, AuthorConfig)
另一个App注册model代码:
1 from file.tool.tag import site, ModelSubject 2 from .models import * 3 from django.utils.safestring import mark_safe 4 from django.shortcuts import reverse 5 # from django.urls import reverse # 我们引用reverse的时候可以使用这两种方式去引用, 6 7 8 class BookConfig(ModelSubject): 9 def edit(self, obj=None, is_header=False): 10 if is_header: 11 return '操作' 12 # 把url改成反向解析: name='%s_%s_standlist' % app_model 13 return mark_safe("<a href='%s'>编辑</a>" % reverse('%s_edit' % self.namespace, args=(obj.pk,))) 14 # 我们的反向解析里面,需要有参数进行拼接,然后我们拼接的参数是我们在url里面的那个name属性的值,name='%s_%s_standlist' % app_model 15 # 按照这个格式我们来把参数一一拼接进去,我们在href里面直接写一个%s,然后再引号外面把reverse给拼接出来, 16 # 然后就是给reverse里面添加参数,它里面有2个参数,第一个参数是App的名字+表名,第二个是我们在使用编辑或者是删除的时候 17 # 需要找到的那个id值,使用args=(,)一个元祖的形式, 18 19 def delete(self, obj=None, is_header=False): 20 if is_header: 21 return '操作' 22 # return mark_safe("<a href='/stark/one/book/%s/dele'>删除</a>" % obj.pk) 23 return mark_safe("<a href='%s'>删除</a>" % reverse('%s_dele' % self.namespace, args=(obj.pk,))) 24 list_display = ['id', 'title', edit, delete] 25 26 27 site.register(Book, BookConfig)

浙公网安备 33010602011771号