- 关键字搜索。 可以做到的效果是, 输入20。 后太通过 Q() 函数。 来实现。 搜索是一个大的问题点。
- 要想实现组合搜索, 首先要 明确的一点是。 在我当前的页面上, 正在进行展示的是 那一张表的数据。
- 并且, 既然是搜索, 那么必然的一点就是。 我的搜索条件, 必须是 和 我这张表中的,字段由关系的。
- 比如, UserInfo 表。 那么 名字就是一个 可以用作搜索的字段。 年龄也是一个可以用作搜索的字段。
- 组合搜索,就是。 将 name 和 age 这两个字段, 甚至更多的字段进行一个组合的搜索。
- 在这个基础上, 我们可以添加上, 模糊一点的条件, 比如 age>20。
先看一下我想做的,这张表把:
class UserInfo(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(verbose_name="用户名", max_length=32) pwd = models.CharField(verbose_name="密码", max_length=16) age = models.CharField(verbose_name="年龄", max_length=3) depart = models.ForeignKey(verbose_name="部门", to="Depart", to_field="id", on_delete=models.CASCADE) class_choice = [ (1, "九年级一班"), (2, "九年级二班"), (3, "九年级三班"), ] classes = models.IntegerField(verbose_name="班级", choices=class_choice, default=1) gender_choice = [ (1, "男"), (2, "女") ] gender = models.IntegerField(verbose_name="性别", choices=gender_choice, default=1) def __str__(self): return self.name
分析得出的结果就是。 这里有7 个字段。 id 先不考虑。 pwd 密码也不考虑。
name 字段。 使用关键字的方式, 进行模糊的查询。
age 字段可以, 做一个区间,让用户来选择。
而 对于, choice 的这种字段。 应该把所有的选项,都罗列出来, 让用户自己选择。
gender 字段,应该做一个, 下拉框。 默认 全部选中。 如果用户选了 男 那么。 就过滤出 男性的人。 反之就是女
classes 字段, 也是一样的。
而对于, ForeignKey 或者 OneToOne ManyToMany 这种的。 应该将被关联的这张表中, 所有的字段, 也做一个同样的展示。
开始搞:
- 基类中配置, 一个 search_group = []
- 和 一个函数 def get_search_group(self): return self.search_group 预留钩子函数,让用户可以进行,权限的不同是否展示的功能
然后在 子类中。 开始进行。 具体的配置项:
class UserInfoHandler(StartHandler): list_display = [StartHandler.display_checkbox, "name", "age", "depart", get_choice_txt("班级", "classes"), get_choice_txt("性别", "gender"), StartHandler.display_edit, StartHandler.display_del] per_page = 10 ordered_list = ["id"] search_list = ["name__contains", "age__gt"] action_list = [StartHandler.action_multi_delete, StartHandler.action_multi_init]
has_add_btn = True
# 配置组合搜索,想要展示哪些, 搜索的条件 search_group = ["gender", "classes", "depart"]
基类的处理:
search_group = [] # 方便,用户自己定制。组合搜索搜索的条件,和如果用户不配置,页面不显示组合搜索 def get_search_group(self): return self.search_group per_page = 10 # 默认每页显示,多少数据。 也可在子类中,自行定制 def check_list_view(self, request, *args, **kwargs): ....... 其他代码在这里不贴了....... # ####################7. 处理组合搜索################### from django.db.models import ForeignKey, ManyToManyField, OneToOneField search_group = self.get_search_group() # ["gender", "classes", "depart"] for item in search_group: # 根据gender或者classes字符串,组自己对应的model类中,找到字段对象,再根据对象,获取关联的数据 field_obj = self.model_class._meta.get_field(item) # 固定用法mate 类,中的get_field() 就可以根据字符串获取对应的对象 # 对field_obj 的类型做判断。 来确定他是一个 choice 还是一个 foreignkey 外键 if isinstance(field_obj, ForeignKey) or isinstance(field_obj, ManyToManyField) or isinstance(field_obj,OneToOneField): # 获取关联表中的, 数据 django1.0 版本使用 field_obj.rel.model.objects.all() field_obj.related_model.objects.all() # django2.0版本使用这种方式 可以拿到, 被关联表的所有数据。 else: # 获取 choice 的数据 field_obj.choices 使用这个对象下的 choices 方法,就能获取到,choices的元组. print(field_obj.choices)
# [(1, '男'), (2, '女')] [(1, '九年级一班'), (2, '九年级二班'), (3, '九年级三班')]
return render(request, "stark/changelist.html", {"header_list": header_list, "data_list": data_list, "body_list": body_list, "pager": pager, "add_btn": add_btn, "search_list": search_list, "search_value": search_value, "action_dict": action_dict})
ok 做到这一部分,基本已经可以实现,组合搜索。 如何从数据库取出数据了! 但是如果可能更加的便捷,并且还可以支持动态的 搜索。 这样就不够了。
因为 子类中 search_group = ["gender", "classes", "depart"] 这里是写死了的, 而且没有任何的条件。 这里只是将所有的数据查询了出来。
并没有像, 比如我要查询 Depart 表中。 id 大于5 的这些数据。 如果要实现这种的,我们可能就需要一个字典。
例: {"field":"depart", "db_condition":{"id__gt":5}} 这种的数据结构。 通过 field字段,查询出对应的表。然后根据 条件dn_condition 来进行条件的,过滤。
这样的话,我在子类中的 search_group 列表, 可能就需要写很多的字典,进去。 而且还有一点,无法做到。根据前端发送过来的数据。进行 动态的筛选。 比如, 我不想要 大于5 了, 我想要 大于3 呢?
所以, 我这里使用了, 在 search_group 类表中,放置一个 对象进行去:
看一看 生成对象的类:
class Option(object): '''使用组合搜索, 想要一些自己的搜索条件。 可扩展可继承''' def __init__(self, field, db_condition=None): ''' :param filed: 组合搜索关联的字段 :param db_condition: 数据库关联查询时查询的条件 ''' self.field = field self.db_condition = db_condition if not db_condition: db_condition = {} self.db_condition = db_condition def get_db_condition(self, request, *args, **kwargs): '''预留继承后的重写函数, 重写后,次基类中的该方法,将被覆盖。 默认返回的是开发者输入的值。''' '''重写后, 可根据,前端的返回值,进行一定的判断''' return self.db_condition def get_queryset_or_tuple(self, model_class, request, *args, **kwargs): '''根据字段去获取数据库关联的数据''' # 根据gender或者classes字符串,组自己对应的model类中,找到字段对象,再根据对象,获取关联的数据 field_obj = model_class._meta.get_field(self.field) # 固定用法mate 类,中的get_field() 就可以根据字符串获取对应的对象 # 对field_obj 的类型做判断。 来确定他是一个 choice 还是一个 foreignkey 外键 if isinstance(field_obj, ForeignKey) or isinstance(field_obj, ManyToManyField): # 获取关联表中的, 数据 django1.0 版本使用 field_obj.rel.model.objects.all() db_condition = self.get_db_condition(request, *args, **kwargs) query_set = field_obj.related_model.objects.filter(**db_condition) print(query_set) else: # 获取 choice 的数据 field_obj.choices print(field_obj.choices)
ok 有个这个类的情况下: 我们在 handler 子类中。 search_group 列表:
search_group = [ Option("gender"), Option("classes"), Option("depart"), ]
有了这个列表, 在视图函数中。 循环的时候, 拿到的就是。 这个对象。
直接调用,对象下的 get_queryset_or_tuple() 就可以了! 并且 这个函数中。
还有一个 获取条件的函数 self.get_db_condition(request, *args, **kwargs): 默认返回的就是一个 空字典。 代表没有任何的条件。
如果我想要加上条件, 也是很简单:
search_group = [ Option("gender"), Option("classes"), Option("depart",{"id__gt":5}), ]
ok 没有问题。 最想要的动态查询呢? 也是很 easy 我只需要 再写一个 子类 并且继承 Option 类,然后重写 get_db_condition 就可以了
class MyOption(Option): '''一个例子,这样重写之后。 Handler类中。 将使用这个方法,来生成对象。 从而覆盖掉基类的方法''' '''并且可以根据,request。 传进来的数据,进行动态的查询。''' def get_db_condition(self, request, *args, **kwargs): return {"id__gt": request.GET.get("nid")}
相应的, serach_group 就需要, 使用这个子类。来构造对象: 并且也不需要在传入,条件参数
search_group = [ Option("gender"), Option("classes"), MyOption("depart"), ]
这样,当对象去调用 get_db_condition 的时候。 会优先使用,自己类中的这个方法。
返回的就是 自己类中的这个方法,返回的 字典。 而且这个字典,是我们通过获取前端发送来的数据,从而进行生成 条件字典。
进而,获取相应的数据。
也就达到了,动态。 获取数据的目的。