123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- # -*- coding: utf-8 -*-
- from __future__ import absolute_import
- from __future__ import division
- from __future__ import print_function
- from __future__ import unicode_literals
- from django.contrib.contenttypes.models import ContentType
- from django.utils.encoding import force_text
- from rapid.registry import registry, Action
- from rapid import filters
- import itertools
- from django.db import models
- from rapid import permissions
- class InstanceData(object):
- def __init__(self, instance, request=None, excludes=None, creator=None, fields=None):
- excludes = [] if excludes is None else excludes
- self.model = ModelData(type(instance), request, excludes, creator, fields)
- self.instance = instance
- self.request = request
- self.excludes = excludes if excludes else []
- self.creator = creator
- self._fields = self.model.fields()
- def values(self):
- r = []
- o = self.instance
- for f in self.model.fields():
- if f.is_relation:
- r.append(self._value_of_field(o, f))
- else:
- r.append(self._value_of_field(o, f))
- return r
- def _value_of_field(self, instance, field):
- """
- Returns the value of the given field.
- ::return A tuple, with the actual value of the field, a boolean that is true iff
- the value is iterable, and a sequence of URLs for the viewers of the instances
- of the first element, or False if there are no viewers.
- """
- if hasattr(instance, field.accessor_name()):
- v = getattr(instance, field.accessor_name())
- else: # Many to many relations without value may disappear
- return [], True
- if hasattr(v, '__iter__'):
- return (v, ()), True
- if hasattr(v, 'all'):
- return [(x, InstanceData(x, self.request, creator=(self, field))) for x in v.all()], True
- if isinstance(v, models.Model):
- return (v, InstanceData(v, self.request, creator=(self, field))), False
- return (v, ()), False
- def fields_and_values(self):
- r = []
- for field in self.model.fields():
- value, is_multiple = self._value_of_field(self.instance, field)
- r.append((field, value, is_multiple))
- return r
- def is_controlled(self):
- return self.model.is_controlled()
- def can_read(self):
- return self.has_permission(self.request, 'view')
- def can_write(self):
- return self.has_permission(self.request, 'edit')
- def view_url(self):
- return registry.get_url_of_action(self.model.model, "view", pk=self.instance.pk)
- def edit_url(self):
- url = registry.get_url_of_action(self.model.model, "edit", pk=self.instance.pk)
- by = self.creator
- if by:
- dt, fd = by
- if fd.one_to_one or fd.one_to_many:
- # Do not change the parent of the viewer.
- return url + "?default=" + fd.field.name + ":" + str(dt.object.pk)
- if fd.many_to_one or fd.many_to_many:
- return url
- return url
- def remove_url(self):
- return registry.get_url_of_action(self.model.model, "delete", pk=self.instance.pk)
- def create_url(self):
- return registry.get_url_of_action(self.model.model, "add")
- def list_url(self):
- return registry.get_url_of_action(self.model.model, "list")
- def select_url(self):
- return registry.get_url_of_action(self.model.model, "select")
- def actions(self):
- r = []
- acts = registry.model_entry(self.model.model)
- if self.request and acts:
- for a in acts.values():
- if self.has_permission(self.request, a.action.name) and\
- a.action.visibility > Action.Visibility.hidden:
- r.append((a, a.get_url(self.instance)))
- return r
- def model_actions(self):
- r = []
- for (a, u) in self.actions():
- if not a.action.query_parameters:
- r.append((a, u))
- return r
- def instance_actions(self):
- r = []
- for (a, u) in self.actions():
- if a.action.query_parameters:
- r.append((a, u))
- return r
- def list_actions(self):
- r = []
- for (a, u) in self.instance_actions():
- if a.action.visibility == Action.Visibility.list:
- r.append((a, u))
- return r
- def has_permission(self, request, action_name):
- m = registry.model_entry(self.model.model).get(action_name)
- if m:
- perm = m.permission.instances
- return permissions.has_instance(self.model, perm(request), self.instance)
- return False
- def __unicode__(self):
- return force_text(self.instance)
- def __str__(self):
- return str(self.model) + ': ' + str(self.instance.pk)
- class ModelData(object):
- def __init__(self, model, request=None, excludes=None, creator=None, fields=None):
- excludes = [] if excludes is None else excludes
- self.model = model
- self.request = request
- self.excludes = excludes if excludes else []
- self.creator = creator
- self._fields = [self.field_by_name(f) for f in fields] if fields else self.all_fields()
- def model_name(self):
- # noinspection PyProtectedMember
- return force_text(self.model._meta.verbose_name)
- def model_name_plural(self):
- # noinspection PyProtectedMember
- return force_text(self.model._meta.verbose_name_plural)
- def default_manager(self):
- # noinspection PyProtectedMember
- return self.model._default_manager
- def all_fields(self):
- r = []
- relations = []
- ignore_fields = []
- for f in itertools.chain(self.local_fields(), self.related_fields()):
- if f.bare_name() in ignore_fields:
- ignore_fields.remove(f.bare_name())
- continue
- if hasattr(f.field, 'is_rapid_alternatives') and f.field.is_rapid_alternatives:
- for ff in self.rapid_alternative_data():
- if ff[1].ct_field == f.bare_name():
- f = FieldData(ff[1])
- for fr in r:
- if fr.bare_name() == ff[1].fk_field:
- r.remove(fr)
- ignore_fields.append(ff[1].fk_field)
- break
- if f.is_relation():
- relations.append(f)
- else:
- if f.name not in self.excludes:
- r.append(f)
- for f in relations:
- if f.name not in self.excludes:
- r.append(f)
- return r
- def fields(self):
- return self._fields
- def local_fields(self):
- """
- :return: FieldData for each local (not related) field of this model.
- """
- r = []
- # noinspection PyProtectedMember
- for f in self.model._meta.local_fields:
- if f.name not in self.excludes:
- r.append(FieldData(f, self.request))
- # noinspection PyProtectedMember
- for f in self.model._meta.local_many_to_many:
- if f.name not in self.excludes:
- r.append(FieldData(f, self.request))
- return r
- def rapid_alternative_data(self):
- """
- :return: Tuples of the form (name, field) with the
- AlternativeData fields of this model.
- Those are not returned by fields()
- """
- for k, v in self.model.__dict__.items():
- if hasattr(v, 'is_rapid_alternatives') and v.is_rapid_alternatives:
- yield (k, v)
- def related_fields(self):
- """
- :return: A FieldData for each related manager for this model.
- """
- # noinspection PyProtectedMember
- return [FieldData(f, self.request) for f in self.model._meta.get_all_related_objects()]
- def is_controlled(self):
- """
- :return: True if this model is already controlled by the rapid registry.
- """
- return registry.is_controlled(self.model)
- def can_read(self):
- if self.can_write():
- return True
- vw = registry.model_entry(self.model)['view'].permission(self.request)
- if vw:
- return vw.exists()
- return False
- def can_write(self):
- ed = registry.model_entry(self.model)['edit'].permission(self.request)
- if ed:
- return ed.exists()
- return False
- def create_url(self):
- """
- :return: URL of the "add" action.
- """
- return registry.get_url_of_action(self.model, "add")
- def list_url(self):
- """
- :return: URL of the "list" action.
- """
- return registry.get_url_of_action(self.model, "list")
- def select_url(self):
- """
- :return: URL of the "select" action.
- """
- return registry.get_url_of_action(self.model, "select")
- def actions(self):
- """
- :return: All actions available for this model (with the object's request).
- """
- r = []
- acts = registry.model_entry(self.model)
- if self.request and acts:
- for a in acts.values():
- if self.has_permission(self.request, a.action.name) and\
- not a.action.query_parameters and\
- a.action.visibility > Action.Visibility.hidden:
- r.append((a, a.get_url()))
- return r
- def has_permission(self, request, action_name):
- m = registry.model_entry(self.model)
- if m:
- a = m.get(action_name)
- if a:
- return bool(a.permission.model(request))
- return False
- def field_by_name(self, field_name):
- """
- :return: A FieldData with the field of this model that has the given name.
- """
- # noinspection PyProtectedMember
- return FieldData(self.model._meta.get_field(field_name), self.request)
- def content_type(self):
- """
- :return: The Django registry ContentType for this model.
- """
- return ContentType.objects.get_for_model(self.model)
- def __unicode__(self):
- return force_text(self.model)
- def __str__(self):
- return 'Model: ' + str(self.model)
- class FieldData(object):
- def __init__(self, field, request=None):
- self.field = field
- self.request = request
- @classmethod
- def from_model(cls, model, field_name):
- ff = ModelData(model).fields()
- for f in ff:
- if f.bare_name() == force_text(field_name):
- return f
- return None
- def bare_name(self):
- return force_text(self.field.name)
- def accessor_name(self):
- if hasattr(self.field, 'get_accessor_name'):
- return force_text(self.field.get_accessor_name())
- return force_text(self.field.name)
- def name(self):
- if self.is_auto() and self.is_relation():
- return self.related_model().model_name_plural() + ' - ' + self.related_field().name()
- if hasattr(self.field, "verbose_name"):
- return force_text(self.field.verbose_name)
- return force_text(self.field.name)
- def name_plural(self):
- if self.is_auto() and self.is_relation():
- return self.related_model().model_name_plural() + ' - ' + self.related_field().name_plural()
- if hasattr(self.field, "verbose_name_plural"):
- return force_text(self.field.verbose_name_plural)
- return self.name() + "s"
- def is_relation(self):
- return self.field.is_relation
- def is_multiple(self):
- if not self.is_relation():
- return False
- if self.field.one_to_many:
- return True
- if self.field.many_to_many:
- return True
- return False
- def related_model(self):
- if hasattr(self.field, "related_model"):
- return ModelData(self.field.related_model)
- if hasattr(self.field, "to"):
- return ModelData(self.field.to)
- return None
- def related_field(self):
- if hasattr(self.field, "field"):
- return FieldData(self.field.field, self.request)
- return None
- def is_auto(self):
- return self.field.auto_created
- def is_existential_dependency(self):
- """
- :return: True if this field determine an existential dependency relation between this model
- and another one.
- That is, returns true if, because of this field, one element of this model can only exist if
- there is one element of one other model.
- Example: A required ForeignKey.
- """
- if not self.is_relation():
- return False
- f = self.field
- if hasattr(f, "many_to_many") and f.many_to_many:
- return False
- if hasattr(f, "many_to_one") and self.field.many_to_one:
- return False
- if hasattr(self.field, "required"):
- return self.field.required
- return True
- def filter_html(self):
- return filters.Filter.selection_type_html(self, self.request)
- def __str__(self):
- return self.bare_name()
- class ValueData(object):
- def __init__(self, value, field):
- self.value = value
- self.field = field
- def can_view(self):
- if self.field.is_relation():
- o = self.field.related_model()
- return registry.is_controlled(o)
- return False
- def is_multiple(self):
- return self.field.is_multiple()
- def __str__(self):
- return str(self.field) + ': ' + str(self.value)
|