Jet fork des offiziellen jet-admin projekts. Geupdated für Django4.
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

474 行
16KB

  1. import datetime
  2. import json
  3. from django.template import Context
  4. from django.utils import translation
  5. from jet import settings
  6. from jet.models import PinnedApplication
  7. try:
  8. from django.apps.registry import apps
  9. except ImportError:
  10. try:
  11. from django.apps import apps # Fix Django 1.7 import issue
  12. except ImportError:
  13. pass
  14. from django.core.serializers.json import DjangoJSONEncoder
  15. from django.http import HttpResponse
  16. try:
  17. from django.core.urlresolvers import reverse, resolve, NoReverseMatch
  18. except ImportError: # Django 1.11
  19. from django.urls import reverse, resolve, NoReverseMatch
  20. from django.contrib.admin import AdminSite
  21. from django.utils.encoding import smart_str as smart_text
  22. from django.utils.text import capfirst
  23. from django.contrib import messages
  24. from django.utils.encoding import force_str as force_text
  25. from django.utils.functional import Promise
  26. from django.contrib.admin.options import IncorrectLookupParameters
  27. from django.contrib import admin
  28. from django.utils.translation import gettext_lazy as _
  29. from django.utils.text import slugify
  30. try:
  31. from collections import OrderedDict
  32. except ImportError:
  33. from ordereddict import OrderedDict # Python 2.6
  34. class JsonResponse(HttpResponse):
  35. """
  36. An HTTP response class that consumes data to be serialized to JSON.
  37. :param data: Data to be dumped into json. By default only ``dict`` objects
  38. are allowed to be passed due to a security flaw before EcmaScript 5. See
  39. the ``safe`` parameter for more information.
  40. :param encoder: Should be an json encoder class. Defaults to
  41. ``django.core.serializers.json.DjangoJSONEncoder``.
  42. :param safe: Controls if only ``dict`` objects may be serialized. Defaults
  43. to ``True``.
  44. """
  45. def __init__(self, data, encoder=DjangoJSONEncoder, safe=True, **kwargs):
  46. if safe and not isinstance(data, dict):
  47. raise TypeError('In order to allow non-dict objects to be '
  48. 'serialized set the safe parameter to False')
  49. kwargs.setdefault('content_type', 'application/json')
  50. data = json.dumps(data, cls=encoder)
  51. super(JsonResponse, self).__init__(content=data, **kwargs)
  52. def get_app_list(context, order=True):
  53. admin_site = get_admin_site(context)
  54. request = context['request']
  55. app_dict = {}
  56. for model, model_admin in admin_site._registry.items():
  57. app_label = model._meta.app_label
  58. try:
  59. has_module_perms = model_admin.has_module_permission(request)
  60. except AttributeError:
  61. has_module_perms = request.user.has_module_perms(app_label) # Fix Django < 1.8 issue
  62. if has_module_perms:
  63. perms = model_admin.get_model_perms(request)
  64. # Check whether user has any perm for this module.
  65. # If so, add the module to the model_list.
  66. if True in perms.values():
  67. info = (app_label, model._meta.model_name)
  68. model_dict = {
  69. 'name': capfirst(model._meta.verbose_name_plural),
  70. 'object_name': model._meta.object_name,
  71. 'perms': perms,
  72. 'model_name': model._meta.model_name
  73. }
  74. if perms.get('view', False):
  75. try:
  76. model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=admin_site.name)
  77. except NoReverseMatch:
  78. pass
  79. if perms.get('change', False):
  80. try:
  81. model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=admin_site.name)
  82. except NoReverseMatch:
  83. pass
  84. if perms.get('add', False):
  85. try:
  86. model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=admin_site.name)
  87. except NoReverseMatch:
  88. pass
  89. if app_label in app_dict:
  90. app_dict[app_label]['models'].append(model_dict)
  91. else:
  92. try:
  93. name = apps.get_app_config(app_label).verbose_name
  94. except NameError:
  95. name = app_label.title()
  96. app_dict[app_label] = {
  97. 'name': name,
  98. 'app_label': app_label,
  99. 'app_url': reverse(
  100. 'admin:app_list',
  101. kwargs={'app_label': app_label},
  102. current_app=admin_site.name,
  103. ),
  104. 'has_module_perms': has_module_perms,
  105. 'models': [model_dict],
  106. }
  107. # Sort the apps alphabetically.
  108. app_list = list(app_dict.values())
  109. if order:
  110. app_list.sort(key=lambda x: x['name'].lower())
  111. # Sort the models alphabetically within each app.
  112. for app in app_list:
  113. app['models'].sort(key=lambda x: x['name'])
  114. return app_list
  115. def get_admin_site(context):
  116. try:
  117. current_resolver = resolve(context.get('request').path)
  118. index_resolver = resolve(reverse('%s:index' % current_resolver.namespaces[0]))
  119. if hasattr(index_resolver.func, 'admin_site'):
  120. return index_resolver.func.admin_site
  121. for func_closure in index_resolver.func.__closure__:
  122. if isinstance(func_closure.cell_contents, AdminSite):
  123. return func_closure.cell_contents
  124. except:
  125. pass
  126. return admin.site
  127. def get_admin_site_name(context):
  128. return get_admin_site(context).name
  129. class LazyDateTimeEncoder(json.JSONEncoder):
  130. def default(self, obj):
  131. if isinstance(obj, datetime.datetime) or isinstance(obj, datetime.date):
  132. return obj.isoformat()
  133. elif isinstance(obj, Promise):
  134. return force_text(obj)
  135. return self.encode(obj)
  136. def get_model_instance_label(instance):
  137. if getattr(instance, "related_label", None):
  138. return instance.related_label()
  139. return smart_text(instance)
  140. class SuccessMessageMixin(object):
  141. """
  142. Adds a success message on successful form submission.
  143. """
  144. success_message = ''
  145. def form_valid(self, form):
  146. response = super(SuccessMessageMixin, self).form_valid(form)
  147. success_message = self.get_success_message(form.cleaned_data)
  148. if success_message:
  149. messages.success(self.request, success_message)
  150. return response
  151. def get_success_message(self, cleaned_data):
  152. return self.success_message % cleaned_data
  153. def get_model_queryset(admin_site, model, request, preserved_filters=None):
  154. model_admin = admin_site._registry.get(model)
  155. if model_admin is None:
  156. return
  157. try:
  158. changelist_url = reverse('%s:%s_%s_changelist' % (
  159. admin_site.name,
  160. model._meta.app_label,
  161. model._meta.model_name
  162. ))
  163. except NoReverseMatch:
  164. return
  165. changelist_filters = None
  166. if preserved_filters:
  167. changelist_filters = preserved_filters.get('_changelist_filters')
  168. if changelist_filters:
  169. changelist_url += '?' + changelist_filters
  170. if model_admin:
  171. queryset = model_admin.get_queryset(request)
  172. else:
  173. queryset = model.objects
  174. list_display = model_admin.get_list_display(request)
  175. list_display_links = model_admin.get_list_display_links(request, list_display)
  176. list_filter = model_admin.get_list_filter(request)
  177. search_fields = model_admin.get_search_fields(request) \
  178. if hasattr(model_admin, 'get_search_fields') else model_admin.search_fields
  179. list_select_related = model_admin.get_list_select_related(request) \
  180. if hasattr(model_admin, 'get_list_select_related') else model_admin.list_select_related
  181. actions = model_admin.get_actions(request)
  182. if actions:
  183. list_display = ['action_checkbox'] + list(list_display)
  184. ChangeList = model_admin.get_changelist(request)
  185. change_list_args = [
  186. request, model, list_display, list_display_links, list_filter,
  187. model_admin.date_hierarchy, search_fields, list_select_related,
  188. model_admin.list_per_page, model_admin.list_max_show_all,
  189. model_admin.list_editable, model_admin]
  190. try:
  191. sortable_by = model_admin.get_sortable_by(request)
  192. change_list_args.append(sortable_by)
  193. except AttributeError:
  194. # django version < 2.1
  195. pass
  196. try:
  197. cl = ChangeList(*change_list_args)
  198. queryset = cl.get_queryset(request)
  199. except IncorrectLookupParameters:
  200. pass
  201. return queryset
  202. def get_possible_language_codes():
  203. language_code = translation.get_language()
  204. language_code = language_code.replace('_', '-').lower()
  205. language_codes = []
  206. # making dialect part uppercase
  207. split = language_code.split('-', 2)
  208. if len(split) == 2:
  209. language_code = '%s-%s' % (split[0].lower(), split[1].upper()) if split[0] != split[1] else split[0]
  210. language_codes.append(language_code)
  211. # adding language code without dialect part
  212. if len(split) == 2:
  213. language_codes.append(split[0].lower())
  214. return language_codes
  215. def get_original_menu_items(context):
  216. if context.get('user') and user_is_authenticated(context['user']):
  217. pinned_apps = PinnedApplication.objects.filter(user=context['user'].pk).values_list('app_label', flat=True)
  218. else:
  219. pinned_apps = []
  220. original_app_list = get_app_list(context)
  221. return map(lambda app: {
  222. 'app_label': app['app_label'],
  223. 'url': app['app_url'],
  224. 'url_blank': False,
  225. 'label': app.get('name', capfirst(_(app['app_label']))),
  226. 'has_perms': app.get('has_module_perms', False),
  227. 'models': list(map(lambda model: {
  228. 'url': model.get('admin_url'),
  229. 'url_blank': False,
  230. 'name': model['model_name'],
  231. 'object_name': model['object_name'],
  232. 'label': model.get('name', model['object_name']),
  233. 'has_perms': any(model.get('perms', {}).values()),
  234. }, app['models'])),
  235. 'pinned': app['app_label'] in pinned_apps,
  236. 'custom': False
  237. }, original_app_list)
  238. def get_menu_item_url(url, original_app_list):
  239. if isinstance(url, dict):
  240. url_type = url.get('type')
  241. if url_type == 'app':
  242. return original_app_list[url['app_label']]['url']
  243. elif url_type == 'model':
  244. models = dict(map(
  245. lambda x: (x['name'], x['url']),
  246. original_app_list[url['app_label']]['models']
  247. ))
  248. return models[url['model']]
  249. elif url_type == 'reverse':
  250. return reverse(url['name'], args=url.get('args'), kwargs=url.get('kwargs'))
  251. elif isinstance(url, str):
  252. return url
  253. def get_menu_items(context):
  254. pinned_apps = PinnedApplication.objects.filter(user=context['user'].pk).values_list('app_label', flat=True)
  255. original_app_list = OrderedDict(map(lambda app: (app['app_label'], app), get_original_menu_items(context)))
  256. custom_app_list = settings.JET_SIDE_MENU_ITEMS
  257. custom_app_list_deprecated = settings.JET_SIDE_MENU_CUSTOM_APPS
  258. if custom_app_list not in (None, False):
  259. if isinstance(custom_app_list, dict):
  260. admin_site = get_admin_site(context)
  261. custom_app_list = custom_app_list.get(admin_site.name, [])
  262. app_list = []
  263. def get_menu_item_app_model(app_label, data):
  264. item = {'has_perms': True}
  265. if 'name' in data:
  266. parts = data['name'].split('.', 2)
  267. if len(parts) > 1:
  268. app_label, name = parts
  269. else:
  270. name = data['name']
  271. if app_label in original_app_list:
  272. models = dict(map(
  273. lambda x: (x['name'], x),
  274. original_app_list[app_label]['models']
  275. ))
  276. if name in models:
  277. item = models[name].copy()
  278. if 'label' in data:
  279. item['label'] = data['label']
  280. if 'url' in data:
  281. item['url'] = get_menu_item_url(data['url'], original_app_list)
  282. if 'url_blank' in data:
  283. item['url_blank'] = data['url_blank']
  284. if 'permissions' in data:
  285. item['has_perms'] = item.get('has_perms', True) and context['user'].has_perms(data['permissions'])
  286. return item
  287. def get_menu_item_app(data):
  288. app_label = data.get('app_label')
  289. if not app_label:
  290. if 'label' not in data:
  291. raise Exception('Custom menu items should at least have \'label\' or \'app_label\' key')
  292. app_label = 'custom_%s' % slugify(data['label'], allow_unicode=True)
  293. if app_label in original_app_list:
  294. item = original_app_list[app_label].copy()
  295. else:
  296. item = {'app_label': app_label, 'has_perms': True}
  297. if 'label' in data:
  298. item['label'] = data['label']
  299. if 'items' in data:
  300. item['items'] = list(map(lambda x: get_menu_item_app_model(app_label, x), data['items']))
  301. if 'url' in data:
  302. item['url'] = get_menu_item_url(data['url'], original_app_list)
  303. if 'url_blank' in data:
  304. item['url_blank'] = data['url_blank']
  305. if 'permissions' in data:
  306. item['has_perms'] = item.get('has_perms', True) and context['user'].has_perms(data['permissions'])
  307. item['pinned'] = item['app_label'] in pinned_apps
  308. return item
  309. for data in custom_app_list:
  310. item = get_menu_item_app(data)
  311. app_list.append(item)
  312. elif custom_app_list_deprecated not in (None, False):
  313. app_dict = {}
  314. models_dict = {}
  315. for app in original_app_list.values():
  316. app_label = app['app_label']
  317. app_dict[app_label] = app
  318. for model in app['models']:
  319. if app_label not in models_dict:
  320. models_dict[app_label] = {}
  321. models_dict[app_label][model['object_name']] = model
  322. app['items'] = []
  323. app_list = []
  324. if isinstance(custom_app_list_deprecated, dict):
  325. admin_site = get_admin_site(context)
  326. custom_app_list_deprecated = custom_app_list_deprecated.get(admin_site.name, [])
  327. for item in custom_app_list_deprecated:
  328. app_label, models = item
  329. if app_label in app_dict:
  330. app = app_dict[app_label]
  331. for model_label in models:
  332. if model_label == '__all__':
  333. app['items'] = models_dict[app_label].values()
  334. break
  335. elif model_label in models_dict[app_label]:
  336. model = models_dict[app_label][model_label]
  337. app['items'].append(model)
  338. app_list.append(app)
  339. else:
  340. def map_item(item):
  341. item['items'] = item['models']
  342. return item
  343. app_list = list(map(map_item, original_app_list.values()))
  344. current_found = False
  345. for app in app_list:
  346. if not current_found:
  347. for model in app['items']:
  348. if not current_found and model.get('url') and context['request'].path.startswith(model['url']):
  349. model['current'] = True
  350. current_found = True
  351. else:
  352. model['current'] = False
  353. if not current_found and app.get('url') and context['request'].path.startswith(app['url']):
  354. app['current'] = True
  355. current_found = True
  356. else:
  357. app['current'] = False
  358. return app_list
  359. def context_to_dict(context):
  360. if isinstance(context, Context):
  361. flat = {}
  362. for d in context.dicts:
  363. flat.update(d)
  364. context = flat
  365. return context
  366. def user_is_authenticated(user):
  367. if not hasattr(user.is_authenticated, '__call__'):
  368. return user.is_authenticated
  369. else:
  370. return user.is_authenticated()