一,保存搜索条件(Save search conditions)

kept conditions(保存条件)的应用场景比较常见,在我们查询时,可以实现多条件的筛选查询,比如:在淘宝上,选择了其中的一个条件后,我们可以继续选择其他的一些过滤条件,然后就可以实现多条件的查询。

那么代码时怎么实现的呢?

我们在用户提交查询条件请求时,一般都是GET请求,在相关路径后拼上?条件的形式,在Django中,我们在后端接收是通过request.GET这个属性得到所有的条件,在Django中,request.GET是一个QueryDict类型的数据。<QueryDict: {}>

#访问一个请求
print(request.GET,type(request.GET)) #<QueryDict: {}> <class 'django.http.request.QueryDict'>

我们点击它的源码,看看这个QueryDict的内部都做了些什么(from django.http.request import QueryDict)

class QueryDict(MultiValueDict):

    _mutable = True
_encoding = None def __init__(self, query_string=None, mutable=False, encoding=None):
super(QueryDict, self).__init__()
if not encoding:
encoding = settings.DEFAULT_CHARSET
self.encoding = encoding
query_string = query_string or '' self._mutable = mutable

这是截取的QueryDict中的一部分,在QueryDict中,有一个属性_mutable,这个值是True时,表示QueryDict可以被修改,否则表示无法被修改,通过源码可以看出,QueryDict在实例一个对象时,self._mutable是False,是不可修改的

而request.GET是一个QueryDict的实例,所以,request.GET是不可以被修改的

如果我们要修改或者添加request.GET中的值时,我们必须设置一个属性值request.GET._mutable=True,这样就可以修改了。

那我们如何保存搜索条件呢,要保存搜索条件,我们首先要得到用户访问时携带的条件,那这个条件怎么获取呢?

在Django中,提供了一个方法用于或者请求的字符串格式的搜索条件,这个方法就是:request.GET.urlencode()

比如:

#我们访问路径:http://127.0.0.1:8000/Xadmin/demo/order/?id=1&page=2

#得到的是这个
print(request.GET,request.GET.urlencode())
<QueryDict: {'id': [''], 'page': ['']}> id=1&page=2

通过这个方法我们可以把获取的这个参数拼接到相应路径后面。

如果我们对request.GET这个参数修改时,我们最好是copy一个同样的数据,而不是在原数据上进行修改,这样可以保证数据的安全性,因为在其他地方,我们肯定还会使用这个request.GET这个参数。

注意:在copy时,我们不需要设置_mutable=True这个参数就可以修改,因为Django已经为我们想到了这个情况,在QueryDict的源码中,对这两个copy有重写,默认的参数就是mutable=True,所以我们使用copy后,可以直接对原数据修改。

 def __copy__(self):
result = self.__class__('', mutable=True, encoding=self.encoding)
for key, value in six.iterlists(self):
result.setlist(key, value)
return result def __deepcopy__(self, memo):
result = self.__class__('', mutable=True, encoding=self.encoding)
memo[id(self)] = result
for key, value in six.iterlists(self):
result.setlist(copy.deepcopy(key, memo), copy.deepcopy(value, memo))
return result

所以,我们在取到这个值时,直接对这个值进行copy,然后在做修改

params = request.GET
import copy
params = copy.deepcopy(params) #可以直接对这个值做修改 params["要修改的值"] = value

二、搜索框功能的实现(search_fields)

相关实现的视图函数部分

#配置类的实现代码
from django.db.models import Q
from django.shortcuts import HttpResponse, render # 配置类
class ModelXadmin(object):
# 配置类中默认的搜索内容
search_fields = [] def __init__(self, model, site):
self.model = model
self.site = site self.model_name = ""
self.app_name = "" # 将搜索的实现单独的封装到一个函数中
def get_search_condition(self, request):
# 实例化一个Q对象
search_condition = Q()
# 设置多条件搜索的关系为或
search_condition.connector = "or"
# print("search_fields", self.search_fields) # ["title","price"]
# 获取输入的搜索内容
key_word = request.GET.get('q')
if key_word: # 对搜索内容判断,如果为空,models.XXX.objects.filter()时 会报错
for search_field in self.search_fields:
search_condition.children.append((search_field + "__icontains", key_word)) return search_condition # 查看视图函数
def list_view(self, request):
"""
self.model: 用户访问哪张表,self.model就是谁
data_list: 查看表下的数据
:param request:
:return:
"""
search_condition = self.get_search_condition(request)
data_list = self.model.objects.filter(search_condition)
model_name = self.model._meta.model_name
return render(request, 'list_view.html', locals())

备注:示例化Q对象值为空时,filter(Q对象) 等同于all()

相关实现的模板部分

{% if search_fields %}
<form action="" class="form-inline">
<div style="margin-bottom: 20px;" class="input-group">
<input type="text" class="form-control" placeholder="请输入内容" name="q">
<span class="input-group-btn">
<button class="btn btn-info" type="submit">搜索</button>
</span>
</div>
</form>
{% endif %}

三、批量操作的实现(actions)

批量操作中相关视图函数的实现

1. 实现的一个基本思路:

  1. 首先,在默认的基础配置类中一定要有一个可设置批量操作的属性,并规定一个实现的方式,自定义的一个格式。
  2. 在后端做一个基础的处理,如果用户为配置该属性,则默认实现批量删除的功能,如果用户自定义了,则优先显示用户自定义的属性,再在最后展示默认的批量删除操作
  3. 构建在前端的展示方式:使用select的下拉框展示option中value为批量操作的方法名(函数名)(备注:因为在前端中不能使用__name__这个属性,所以在后端构建),文本内容为对这个方法的描述内容
  4. 将所有的有效控件放置在一个form表单中,发送post请求,(有效控件包括:select标签中的option标签:所有的批量操作的方法action,表单中的每行的checkbox,代表该行数据的pk值)
  5. 取到对应的方法名(字符串形式的)和对应的数据,通过反射执行该方法,进行相应批量操作
  6. 返回处理结果,或者直接展示当前页面

2.具体实现代码:

class ModelXadmin(object):
actions=[] # 默认的批量删除操作,默认出入两个参数:1.request 2.包含所有执行批量操作数据的queryset集
def patch_delete(self,request,queryset):
queryset.delete() # 方法的描述内容
patch_delete.short_description="批量删除" # action的基础处理 优先展示用户的配置信息
def get_actions(self):
temp=[]
temp.extend(self.actions)
temp.append(self.patch_delete) return temp def __init__(self,model,site): self.model=model
self.site=site self.model_name=""
self.app_name=""

构建展示方式:

class ShowList(object):
def __init__(self,config,request,data_list): self.config=config
self.data_list=data_list
self.request=request # 封装的展示类中
self.actions=self.config.get_actions() # 处理前端的展示信息 构建一个包含每一个action字典的列表
def new_actions(self):
temp=[]
for action in self.actions:
temp.append({
"name":action.__name__,
"desc":action.short_description
}) return temp

批量操作中相关模板部分的实现

<form action="" method="post">
{% csrf_token %}
<div>
<select name="action" id="" class="form-control" style="display: inline-block;width: 300px">
<option value="">---------------</option> {% for action_dict in show_list.new_actions %}
<option value="{{ action_dict.name }}">{{ action_dict.desc }}</option>
{% endfor %} </select>
<input type="submit" value="Go" class="btn btn-warning"> </div>
<table class="table table-bordered table-striped">
<thead>
<tr>
{% for item in show_list.get_header %}
<th>{{ item }}</th>
{% endfor %} </tr>
</thead>
<tbody>
{% for new_data in show_list.get_body %}
<tr>
{% for item in new_data %}
<td>{{ item }}</td>
{% endfor %}
</tr>
{% endfor %} </tbody>
</table> </form>

发送post请求后,后端处理:

class ModelXadmin(object):
def __init__(self, model, site):
self.model = model
self.site = site def list_view(self, request):
"""
self.model: 用户访问哪张表,self.model就是谁
data_list: 查看表下的数据
ShowList(self,data_list) # self: 当前查看表的配置类对象
:param request:
:return:
""" if request.method == "POST": # action
action = request.POST.get("action")
selected_pk_list = request.POST.getlist("selected") # 多选框时使用getlist方法接收所有的值
queryset = self.model.objects.filter(pk__in=selected_pk_list) # 过滤符合条件的所有的数据
action = getattr(self, action) # 通过反射获取结果
ret = action(request, queryset) return ret

四、过滤器的实现(list_filter)

1.过滤器实现的基本思路:

  1. 基础配置类中配置过滤的相关属性,以及适用方式(输入要过滤的字段即可,一般输入FK字段,M2M字段,其他字段没有意义)
  2. 处理配置信息,构建展示方式(后端生成a标签,实现保存搜索条件和相应条件对应的对象显示突出,即用户点那个那个条件变色)
  3. 接收过滤对象,后端进行筛选数据,同search_fields

2.具体的代码实现:

class ModelXadmin(object):
list_filter=[]

构建展示内容:

from copy import deepcopy
from django.utils.safestring import mark_safe class ShowList(object):
def __init__(self,config,request,data_list): self.config=config
self.data_list=data_list
self.request=request # list_filter
self.list_filter=self.config.list_filter # ["publish","auhtors] def get_filter_link_tags(self): # 构建的最终方式:link_tags={"publish":["a标签","a标签"],"author":["a标签","a标签"]}
link_tags={} # 循环每一个过滤条件
for filter_field in self.list_filter: # ["publish","auhtors] # 保存过滤条件 不要对原数据修改 保证数据的安全性
params = deepcopy(self.request.GET) # {"authors":2} # 构建方式中a标签的href是:?过滤字段名 = 选中对象的pk值
# 获取当前选中的对象 注意:必须从原数据中取,原数据是不变的 deepcopy的数据是可变的
current_id=self.request.GET.get(filter_field)
# print("current_id",current_id) # 获取当前字段对象
filter_field_obj=self.config.model._meta.get_field(filter_field) # 获取当前字段对象对应的所有的数据
related_data_list=filter_field_obj.rel.to.objects.all()
temp=[]
for obj in related_data_list:
params[filter_field]=obj.pk # 保存搜索条件用
_url=params.urlencode() # 获取字符串形式的参数
# 如果当前对象 = 搜索条件中的对象 做特殊展示
if current_id==str(obj.pk): # 备注:obj.pk 是数字类型,一定要做类型转换
s="<a class='item' href='?%s'>%s</a>"%(_url,str(obj))
else:
s = "<a href='?%s'>%s</a>" % (_url, str(obj)) temp.append(mark_safe(s)) link_tags[filter_field]=temp return link_tags

对用户选择的过滤条件做筛选处理

class ModelXadmin(object):

    def list_view(self, request):

        filter_condition = Q()

        for filter_field, val in request.GET.items():
# 报存搜索条件处理时,因为还会有其他的干扰条件,必须过滤掉,比如:分页的page参数 搜索框的q参数等
if filter_field not in ["page", "q"]:
filter_condition.children.append((filter_field, val)) data_list = self.model.objects.filter(filter_condition)

五、其他功能实现(展示字段的实现、展示字段可点击跳转的实现、增删改查)

from django.shortcuts import HttpResponse,render,redirect
from django.urls import reverse
from django.utils.safestring import mark_safe class ShowList(object):
def __init__(self,config,request,data_list): self.config=config
self.data_list=data_list
self.request=request # 分页器组件相关配置
current_page=request.GET.get("page")
all_count=self.data_list.count()
pagination=Pagination(current_page,all_count,request.GET)
self.pagination=pagination
self.page_data_list=self.data_list[pagination.start:pagination.end] def get_header(self):
# 处理表头
# header_list=["ID","书籍名称","出版社"]
header_list = [] for field in self.config.new_list_display(): # [check,"nid","title","publish",edit,delete]
if isinstance(field, str):
if field == "__str__":
val = self.config.model._meta.model_name.upper()
else:
field_obj = self.config.model._meta.get_field(field)
val = field_obj.verbose_name else:
val = field(self.config, is_header=True) # 获取表头,传is_header=True header_list.append(val) return header_list def get_body(self): # 处理表单数据
new_data_list = []
for obj in self.page_data_list: # data_list [book_obj,book_obj2,...] temp = []
for field in self.config.new_list_display(): # ["nid","title","publish","authors"]
if isinstance(field, str):
try:
from django.db.models.fields.related import ManyToManyField field_obj = self.config.model._meta.get_field(field) if isinstance(field_obj, ManyToManyField):
t = []
for i in getattr(obj, field).all():
t.append(str(i))
val = ",".join(t)
else:
if field in self.config.list_display_link:
edit_url = self.config.get_edit_url(obj)
val = mark_safe("<a href='%s'>%s</a>" % (edit_url, getattr(obj, field)))
else:
val = getattr(obj, field) except Exception as e:
val = getattr(obj, field) else:
val = field(self.config, obj) temp.append(val) new_data_list.append(temp) '''
new_data_list=[
["北京折叠",122,<a href=''>编辑</a>,<a href=''>删除</a>],
["三体", 222,<a href=''>编辑</a>,<a href=''>删除</a>],
]
''' return new_data_list class ModelXadmin(object):
list_display=["__str__",]
list_display_link=[] model_form_class=None def __init__(self,model,site): self.model=model
self.site=site self.model_name=""
self.app_name="" # 选择按钮 编辑 删除
def edit(self, obj=None, is_header=False):
if is_header:
return "操作"
_url=self.get_edit_url(obj)
return mark_safe("<a href='%s'>编辑</a>" % _url) def delete(self, obj=None, is_header=False): if is_header:
return "操作" _url=self.get_delete_url(obj) return mark_safe("<a href='%s'>删除</a>"%_url) def checkbox(self, obj=None, is_header=False):
if is_header:
return "选择" return mark_safe("<input type='checkbox' name='selected' value='%s'>"%obj.pk) # 反向解析当前表的增删改查的url
def get_edit_url(self,obj):
# 反向解析:url
url_name = "%s_%s_change" % (self.app_name, self.model_name)
# http://127.0.0.1:8008/Xadmin/app01/book/(\d+)/change
_url = reverse(url_name, args=(obj.pk,))
# return mark_safe("<a href='%s/change/'>编辑</a>"%obj.pk)
return _url def get_list_url(self):
# 反向解析:url
url_name = "%s_%s_list" % (self.app_name, self.model_name)
_url = reverse(url_name)
return _url def get_add_url(self):
# 反向解析:url
url_name = "%s_%s_add" % (self.app_name, self.model_name)
_url = reverse(url_name)
return _url def get_delete_url(self,obj):
# 反向解析:url
url_name = "%s_%s_delete" % (self.app_name, self.model_name)
_url = reverse(url_name, args=(obj.pk,))
return _url # 构建新的展示列表,默认加入选择按钮 编辑 删除
def new_list_display(self):
temp=[] temp.append(ModelXadmin.checkbox)
temp.extend(self.list_display)
if not self.list_display_link:
temp.append(ModelXadmin.edit)
temp.append(ModelXadmin.delete) return temp # 查看视图函数
def list_view(self, request):
"""
self.model: 用户访问哪张表,self.model就是谁
data_list: 查看表下的数据
ShowList(self,data_list) # self: 当前查看表的配置类对象
:param request:
:return:
""" data_list = self.model.objects.all() show_list=ShowList(self,request,data_list)
add_url = self.get_add_url()
model_name = self.model._meta.model_name return render(request, 'list_view.html',locals()) # 获取modelForm类
def get_model_form_class(self):
if self.model_form_class:
return self.model_form_class
else:
# 使用Django中ModelForm实现展示所有字段
from django.forms import ModelForm
class DemoModelForm(ModelForm):
class Meta:
model=self.model
fields="__all__"
return DemoModelForm # 添加视图函数
def add_view(self, request):
DemoModelForm = self.get_model_form_class()
if request.method=="POST":
form=DemoModelForm(request.POST)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
else:
return render(request, 'add_view.html', locals()) form=DemoModelForm return render(request, 'add_view.html',locals()) # 编辑视图函数
def change_view(self, request, id):
edit_obj=self.model.objects.get(pk=id)
DemoModelForm=self.get_model_form_class() if request.method=="POST":
form=DemoModelForm(request.POST,instance=edit_obj)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
else:
return render(request, 'change_view.html', locals())
form=DemoModelForm(instance=edit_obj)
return render(request, 'change_view.html',locals()) # 删除视图函数
def delete_view(self, request, id):
list_url = self.get_list_url() if request.method=="POST":
self.model.objects.filter(pk=id).delete()
return redirect(list_url) return render(request, 'delete_view.html',{"list_url":list_url})

六、添加页面中特殊字段的小窗口的动态添加的实现

1.实现的基本思路:

  1. 如何在一对多、多对多或者一对一的字段后显示添加按钮
  2. 如何弹出一个窗口
  3. 如何将新窗口中添加的数据绑定到老窗口

基本思路的相关实现:如何辨别这个字段是一个普通的字段还是一个特殊的字段?Django中我们可以循环每一个字段对象,判断是不是这个类型的对象即可,以ModelForm为例,我们使用ModelForm示例一个对象,form_obj=ModelForm(),循环这个对象,一个元素就是一个字段对象,print(type(field))  得到的是一个BoundField的对象:<class 'django.forms.boundfield.BoundField'>

from django.forms import ModelForm

form_obj = ModelForm()

for field in form_obj:
print(type(field)) 结果为:
<class 'django.forms.boundfield.BoundField'>
<class 'django.forms.boundfield.BoundField'>
<class 'django.forms.boundfield.BoundField'>
<class 'django.forms.boundfield.BoundField'>
... # 每一个字段对象都是一个boundfield对象。

  显而易见,boundfield对原本的字段对象做了封装 ,我们打开它的源码,看看都有哪些可用的东西:

class BoundField(object):
"A Field plus data"
def __init__(self, form, field, name):
self.form = form
self.field = field
self.name = name
self.html_name = form.add_prefix(name)
self.html_initial_name = form.add_initial_prefix(name)
self.html_initial_id = form.add_initial_prefix(self.auto_id)
if self.field.label is None:
self.label = pretty_name(name)
else:
self.label = self.field.label
self.help_text = field.help_text or '' @property
def auto_id(self):
"""
Calculates and returns the ID attribute for this BoundField, if the
associated Form has specified auto_id. Returns an empty string otherwise.
"""
auto_id = self.form.auto_id
if auto_id and '%s' in force_text(auto_id):
return force_text(auto_id) % self.html_name
elif auto_id:
return self.html_name
return '' @property
def id_for_label(self):
"""
Wrapper around the field widget's `id_for_label` method.
Useful, for example, for focusing on this field regardless of whether
it has a single widget or a MultiWidget.
"""
widget = self.field.widget
id_ = widget.attrs.get('id') or self.auto_id
return widget.id_for_label(id_)

这是截取的一段源码:我们可以利用的有一个对象属性:self.field = field,还有一个静态方法:auto_id 它们有什么作用呢 ?print一下就知道了:

for boundfield in form_obj:
print(boundfield.field)
print(boundfield.auto_id) 结果为:
<django.forms.fields.CharField object at 0x000002AED2E33BA8>
id_title
<django.forms.fields.CharField object at 0x000002AED2E33A58>
id_desc
<django.forms.models.ModelChoiceField object at 0x000002AED2E33B00>
id_publish
<django.forms.models.ModelMultipleChoiceField object at 0x000002AED2E33B38>
id_authors

由此可见:boundfield.field  返回的是该字段的对象       boundfield.auto_id 返回一个 id_字段名      这种格式的字符串  也就是对应生成HTML标签时的id值

回到正题,我们的目的是通过代码过滤出这些特殊的一对多、多对多或者一对一字段,那怎么实现呢:直接判断是不是这个类型,额外的一个知识点就是,这几个特殊的字段在ModelForm中都是ModelChoiceField的对象,或者继承这个类的对象,我们判断是不是这个类的对象,就可以实现对这个的多虑,

代码实现:

from django.forms.models import ModelChoiceField
for boundfield in form_obj:

            if isinstance(boundfield.field,ModelChoiceField):
# 如果是这个类的对象,就给这个对象设置一个区分其他对象的属性,比如:
boundfield.is_field = True # obj.is_field=值

HTML的实现:

{% for field in form_obj %}
<label for="">{{ field.label }}</label>
{% if field.is_field %}
<p>{{ field }} <span style="font-size:32px;font-weight: 500">+添加按钮在此</span></p>
{% else %}
<p>{{ field }}</p>
{% endif %}
<span>{{ field.errors.0 }}</span>
{% endfor %}

这样我们就实现了第一步,那接下来第二步:打开一个窗口

基本思路:点击添加按钮,触发一个打开一个新窗口的事件:

那么这个新窗口的路径是什么?应该是我们点击那个字段,就打开那个字段相对应的添加页面,怎么实现呢?还是回到上面,我们循环每一个字段时,如果为特殊的字段,就获取这个字段所对应的模型表,和所在的app,然后通过反向解析url,来或者当前这个字段的添加url,然后添加到这个对象的属性中,直接在模板语言中获取。

备注:这里有个小问题,那就是,如果我们没有将当前字段所在的模型表注册时,就不应该显示可添加按钮,同时,在反向解析url时,就会报错,因为没有注册,就没有这个模型类的相关url,也就没有反向解析这个东西了。处理很简单,加一个判断,那怎么判断?一个思路是,判断当前字段所在的模型表,在不在注册的字典中  site._registry,首先,怎么获取当前字段所在模型表:一个知识点:取得字段对象,有一个queryset的属性,这个queryset会获取到当前字段在数据库中所有的数据,在.model 就可以或者当前表,这个方法只对这些特殊字段可用!!!

 for boundfield in form_obj:
if isinstance(boundfield.field,ModelChoiceField):
print(boundfield.field.queryset.model) 输出结果为: <class 'blogs.models.Publisher'>
<class 'blogs.models.Author'>

我们通过这个结果: ._meta.app_label  获得当前字段所在app的字符串名,   ._meta.model_name  得到当前字段所在的模型表的字符串名。

通过这个模型类,判断在不在注册的模型字典中:

视图函数的相关实现:

        for boundfield in form_obj:
if isinstance(boundfield.field,ModelChoiceField):
          # 如果在注册表中,就添加属性
if boundfield.field.queryset.model in site._registry:
boundfield.is_field = True
ret = boundfield.field.queryset.model._meta
root = '%s_%s_add'%(ret.app_label,ret.model_name)
root_url = reverse(root)
boundfield.root_url = root_url+"?pop_back_id=%s"%boundfield.auto_id  
                # 拼接一个参数,用于将添加的数据添加到老窗口中,服务于第三步

HTML页面的相关实现:

<form action="" method="post" novalidate>
{% csrf_token %} {% for filed in form %}
<div class="form-group" style="position: relative">
<label for="">{{ filed.label }}</label>
{{ filed }} <span>{{ filed.errors.0 }}</span> {% if filed.is_field %}
<a onclick="pop('{{ filed.root_url }}')"><span style="font-size: 22px;color: #1b6d85;
                  position: absolute;top: 27px;right:-23px">+</span></a>
{% endif %} </div>
{% endfor %} <input type="submit" class="btn btn-default"> </form> <script> function pop(url) {
window.open(url,"","wdith=600,height=300,top=200,left=200")
} </script>

第三步的实现:提交添加数据后,数据创建成功后我们可以获取到一个新数据的obj,然后通过判断路径中有没有参数pop_back_id对应的值,上面设置路径时,添加的一个区分的一个标记。如果有值,表示是小窗口提交的值,否则表示正常的一个添加页面,如果是正常的添加页面,不做其他处理,如果是小窗口提交的数据,我们可以返回一个关闭窗口的HTML代码,同时可以向老窗口传参,将pk值,str(obj),以及是哪个字段发起的添加,就把数据添加到哪个数据下的标识:

视图函数部分:

def add_view(self, request):

        DemoModelForm = self.get_model_form_class()
if request.method=="POST":
form=DemoModelForm(request.POST)
if form.is_valid():
obj=form.save() pop_back_id=request.GET.get("pop_back_id") if pop_back_id: # 小窗口 text=str(obj)
return render(request,"pop.html",locals()) else: # 大窗口
return redirect(self.get_list_url())

HTML部分:

pop.html 页面:用于关闭这个小窗口,同时向老窗口传值

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <script> window.opener.handle_pop("{{ obj.pk }}","{{ text }}","{{ pop_back_id }}"); window.close() </script> </body>
</html>

原添加HTML页面执行对应的添加数据函数

<script>

    function handle_pop(pk,text,pop_id) {

          var $option=$("<option>"); // <option></option>  创建一个option标签
$option.html(text); // 添加文本值
$option.attr("value",pk);
$option.attr("selected","selected"); // 添加默认选中的效果
$("#"+pop_id).append($option); // 将这个数据添加到对应的标签中 } </script>

总结:

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from django.urls import reverse
from django.utils.safestring import mark_safe
from Xadmin.utils.page import Pagination
from django.db.models import Q
class ShowList(object):
def __init__(self,config,request,data_list): self.config=config
self.data_list=data_list
self.request=request # 分页器组件相关配置
current_page=request.GET.get("page")
all_count=self.data_list.count()
pagination=Pagination(current_page,all_count,request.GET)
self.pagination=pagination
self.page_data_list=self.data_list[pagination.start:pagination.end] self.actions=self.config.get_actions() # list_filter self.list_filter=self.config.list_filter # ["publish","auhtors] def get_filter_link_tags(self): #link_tags={"publish":["a","a"],"author":["a","a"]} link_tags={} from copy import deepcopy for filter_field in self.list_filter: # ["publish","auhtors] params = deepcopy(self.request.GET) # {"authors":2} current_id=self.request.GET.get(filter_field)
print("current_id",current_id) filter_field_obj=self.config.model._meta.get_field(filter_field) related_data_list=filter_field_obj.rel.to.objects.all()
temp=[]
for obj in related_data_list:
params[filter_field]=obj.pk _url=params.urlencode() if current_id==str(obj.pk):
s="<a class='item' href='?%s'>%s</a>"%(_url,str(obj))
else:
s = "<a href='?%s'>%s</a>" % (_url, str(obj)) temp.append(mark_safe(s)) link_tags[filter_field]=temp return link_tags def new_actions(self):
temp=[]
for action in self.actions:
temp.append({
"name":action.__name__,
"desc":action.short_description
}) print("temp",temp) return temp def get_header(self):
# 处理表头
# header_list=["ID","书籍名称","出版社"]
header_list = [] for field in self.config.new_list_display(): # [check,"nid","title","publish",edit,delete]
if isinstance(field, str):
if field == "__str__":
val = self.config.model._meta.model_name.upper()
else:
field_obj = self.config.model._meta.get_field(field)
val = field_obj.verbose_name else:
val = field(self.config, is_header=True) # 获取表头,传is_header=True header_list.append(val) return header_list def get_body(self): # 处理表单数据
new_data_list = []
for obj in self.page_data_list: # data_list [book_obj,book_obj2,...] temp = []
for field in self.config.new_list_display(): # ["nid","title","publish","authors"]
if isinstance(field, str):
try:
from django.db.models.fields.related import ManyToManyField field_obj = self.config.model._meta.get_field(field) if isinstance(field_obj, ManyToManyField):
t = []
for i in getattr(obj, field).all():
t.append(str(i))
val = ",".join(t)
else:
if field in self.config.list_display_link:
edit_url = self.config.get_edit_url(obj)
val = mark_safe("<a href='%s'>%s</a>" % (edit_url, getattr(obj, field)))
else:
val = getattr(obj, field) except Exception as e:
val = getattr(obj, field) else:
val = field(self.config, obj) temp.append(val) new_data_list.append(temp) '''
new_data_list=[
["北京折叠",122,<a href=''>编辑</a>,<a href=''>删除</a>],
["三体", 222,<a href=''>编辑</a>,<a href=''>删除</a>],
]
''' return new_data_list class ModelXadmin(object):
list_display=["__str__",]
list_display_link=[]
search_fields=[]
actions=[]
list_filter=[] def patch_delete(self,request,queryset):
queryset.delete() patch_delete.short_description="批量删除" def get_actions(self):
temp=[]
temp.extend(self.actions)
temp.append(self.patch_delete) return temp model_form_class=None def __init__(self,model,site): self.model=model
self.site=site self.model_name=""
self.app_name="" # 选择按钮 编辑 删除
def edit(self, obj=None, is_header=False):
if is_header:
return "操作"
_url=self.get_edit_url(obj)
return mark_safe("<a href='%s'>编辑</a>" % _url) def delete(self, obj=None, is_header=False): if is_header:
return "操作" _url=self.get_delete_url(obj) return mark_safe("<a href='%s'>删除</a>"%_url) def checkbox(self, obj=None, is_header=False):
if is_header:
return "选择" return mark_safe("<input type='checkbox' name='selected' value='%s'>"%obj.pk) # 反向解析当前表的增删改查的url
def get_edit_url(self,obj):
# 反向解析:url
url_name = "%s_%s_change" % (self.app_name, self.model_name)
# http://127.0.0.1:8008/Xadmin/app01/book/(\d+)/change
_url = reverse(url_name, args=(obj.pk,))
# return mark_safe("<a href='%s/change/'>编辑</a>"%obj.pk)
return _url def get_list_url(self):
# 反向解析:url
url_name = "%s_%s_list" % (self.app_name, self.model_name)
_url = reverse(url_name)
return _url def get_add_url(self):
# 反向解析:url
url_name = "%s_%s_add" % (self.app_name, self.model_name)
_url = reverse(url_name)
return _url def get_delete_url(self,obj):
# 反向解析:url
url_name = "%s_%s_delete" % (self.app_name, self.model_name)
_url = reverse(url_name, args=(obj.pk,))
return _url # 构建新的展示列表,默认加入选择按钮 编辑 删除
def new_list_display(self):
temp=[] temp.append(ModelXadmin.checkbox)
temp.extend(self.list_display)
if not self.list_display_link:
temp.append(ModelXadmin.edit)
temp.append(ModelXadmin.delete) return temp def get_search_condition(self,request): search_condition = Q()
search_condition.connector = "or"
print("search_fields", self.search_fields) # ["title","price"] key_word = request.GET.get('q')
if key_word:
for search_field in self.search_fields:
search_condition.children.append((search_field + "__icontains", key_word)) return search_condition # 查看视图函数
def list_view(self, request):
"""
self.model: 用户访问哪张表,self.model就是谁
data_list: 查看表下的数据
ShowList(self,data_list) # self: 当前查看表的配置类对象
:param request:
:return:
""" if request.method=="POST":# action
print(request.POST)
action=request.POST.get("action")
selected_pk_list=request.POST.getlist("selected")
queryset=self.model.objects.filter(pk__in=selected_pk_list)
action=getattr(self,action)
ret=action(request,queryset) return ret search_condition=self.get_search_condition(request) filter_condition=Q() for filter_field,val in request.GET.items():
if filter_field not in ["page","q"]:
filter_condition.children.append((filter_field,val)) data_list = self.model.objects.filter(filter_condition).filter(search_condition) show_list=ShowList(self,request,data_list)
add_url = self.get_add_url()
model_name = self.model._meta.model_name return render(request, 'list_view.html', {"model_name":model_name,"add_url":add_url,"show_list":show_list}) # 获取modelForm类
def get_model_form_class(self): if self.model_form_class: return self.model_form_class else: from django.forms import ModelForm class DemoModelForm(ModelForm):
class Meta:
model=self.model
fields="__all__" return DemoModelForm # 添加视图函数
def add_view(self, request): DemoModelForm = self.get_model_form_class()
if request.method=="POST":
form=DemoModelForm(request.POST)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
else:
return render(request, 'add_view.html', locals()) form=DemoModelForm return render(request, 'add_view.html',locals()) # 编辑视图函数
def change_view(self, request, id):
edit_obj=self.model.objects.get(pk=id)
DemoModelForm=self.get_model_form_class() if request.method=="POST":
form=DemoModelForm(request.POST,instance=edit_obj)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
else:
return render(request, 'change_view.html', locals())
form=DemoModelForm(instance=edit_obj)
return render(request, 'change_view.html',locals()) # 删除视图函数
def delete_view(self, request, id):
list_url = self.get_list_url() if request.method=="POST":
self.model.objects.filter(pk=id).delete()
return redirect(list_url) class Per():
def __init__(self):
pass return render(request, 'delete_view.html',{"list_url":list_url}) def get_urls2(self):
temp = [] self.app_name = self.model._meta.app_label # "app01"
self.model_name = self.model._meta.model_name # "book"
temp.append(url(r"^$", self.list_view,name="%s_%s_list"%(self.app_name,self.model_name)))
temp.append(url(r"^add/$", self.add_view,name="%s_%s_add"%(self.app_name,self.model_name)))
temp.append(url(r"^(\d+)/change/$", self.change_view,name="%s_%s_change"%(self.app_name,self.model_name)))
temp.append(url(r"^(\d+)/delete/$", self.delete_view,name="%s_%s_delete"%(self.app_name,self.model_name))) return temp # 路由二级分发
@property
def urls2(self):
return self.get_urls2(), None, None class XadminSite(object): def __init__(self, name='admin'):
self._registry = {} def get_urls(self): print(self._registry) # {Book:modelAdmin(Book),.......} temp = []
for model, admin_class_obj in self._registry.items():
# 获取当前循环的model的字符串与所在app的字符串
app_name = model._meta.app_label # "app01"
model_name = model._meta.model_name # "book" temp.append(url(r'^{0}/{1}/'.format(app_name, model_name),admin_class_obj.urls2), ) '''
url(r"app01/book",ModelXadmin(Book,site).urls2)
url(r"app01/publish",ModelXadmin(Publish,site).urls2)
url(r"app02/order",ModelXadmin(Order,site).urls2) '''
return temp @property
def urls(self):
return self.get_urls(),None,None def register(self, model, admin_class=None, **options):
if not admin_class:
admin_class = ModelXadmin self._registry[model] = admin_class(model, self) # {Book:ModelAdmin(Book),Publish:ModelAdmin(Publish)} site=XadminSite()

后端逻辑处理

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/list_view.css">
</head>
<body> <h3>查看{{ model_name }}数据</h3> <div class="container">
<div class="row"> <div class="col-md-9">
<a href="{{ add_url }}" class="btn btn-info add_btn">添加</a>
{% if show_list.config.search_fields %}
<form action="" class="pull-right" method="get">
<input style="display: inline-block;width: 300px" type="text" name="q" class="form-control"><input type="submit" value="search" class="btn btn-success">
</form>
{% endif %} <form action="" method="post">
{% csrf_token %}
<div>
<select name="action" id="" class="form-control" style="display: inline-block;width: 300px">
<option value="">---------------</option> {% for action_dict in show_list.new_actions %}
<option value="{{ action_dict.name }}">{{ action_dict.desc }}</option>
{% endfor %} </select>
<input type="submit" value="Go" class="btn btn-warning"> </div>
<table class="table table-bordered table-striped">
<thead>
<tr>
{% for item in show_list.get_header %}
<th>{{ item }}</th>
{% endfor %} </tr>
</thead>
<tbody>
{% for new_data in show_list.get_body %}
<tr>
{% for item in new_data %}
<td>{{ item }}</td>
{% endfor %}
</tr>
{% endfor %} </tbody>
</table> </form> <div class="pull-right"> {{ show_list.pagination.page_html|safe }}</div>
</div> <div class="col-md-3">
{% for filter_filed,link_tag_list in show_list.get_filter_link_tags.items %}
<p>By {{ filter_filed.upper }}</p>
{% for link_tag in link_tag_list %}
<p>{{ link_tag }}</p>
{% endfor %} {% endfor %} </div>
</div> </div> </body>
</html>

前端展示页面

最新文章

  1. ios 父VIew的宽度 等于较大view的宽度,并且垂直居中
  2. 【C#】线程之Parallel
  3. 004--VS C++ 绘制封闭图形
  4. WebBrowser控件应用:弹出新窗体和关闭窗口
  5. Java实体书写规范
  6. c#调用系统资源大集合-2
  7. 1.6.6 De-Duplication(重复数据删除)
  8. java.lang.IllegalArgumentException: Service Intent must be explicit: Intent
  9. CentOS 7 命令备忘录
  10. UVA - 11324 The Largest Clique 强连通缩点+记忆化dp
  11. Linux操作系统学习_操作系统是如何工作的
  12. 【1414软工助教】团队作业3——需求改进&amp;系统设计 得分榜
  13. MOOS学习笔记1——HelloWorld
  14. windows下mongodb安装详解
  15. CentOS---zabbix使用sendEamil发送报警
  16. Lr-代理录制
  17. mavean项目的jar位置的影响
  18. 普通方法调用,Invoke,begininvoke三者的区别总结及异步与同步的区别总结
  19. 讨论!MyBatis中利用package自动扫描包中的类,默认别名不只是首字母小写!
  20. windows下libcurl+openssl编译与使用配置

热门文章

  1. Telegram Groups vs Telegram Channels
  2. Docker存储容易忽略的使用细节
  3. Centos7服务器环境搭建
  4. Centos7下 升级php5.4到7.1 yum安装
  5. zotero入门简介
  6. Pangu and Stones(HihoCoder-1636)(17北京OL)【区间DP】
  7. Laravel三种中间件的作用
  8. swiper手滑动轮播图后自动轮播失效解决办法
  9. JWT与Session比较和作用
  10. BurpSuite 爆破网页后台登陆