def get_or_create_quota(self, scope): if not self._can_quota_be_created_for_scope(scope): raise exceptions.CreationConditionFailedQuotaError( 'Wrong scope: Cannot create quota "%s" for scope "%s".' % (self.name, scope)) defaults = { 'limit': self.default_limit(scope) if six.callable(self.default_limit) else self.default_limit, 'usage': self.default_usage(scope) if six.callable(self.default_usage) else self.default_usage, } return scope.quotas.get_or_create(name=self.name, defaults=defaults)
def get_resource_for_object(obj): """Return the resource for an object. Args: obj: The object whose model has a resource associated. Returns: WebAPIResource: The resource associated with the object, or ``None`` if not found. """ from djblets.webapi.resources.base import WebAPIResource cls = obj.__class__ # Deferred models are a subclass of the actual model that we want to look # up. if getattr(obj, '_deferred', False): cls = cls.__bases__[0] resource = _model_to_resources.get(cls, None) if not isinstance(resource, WebAPIResource) and six.callable(resource): resource = resource(obj) return resource
def render_data(self, state, obj): """Renders the column data to a string. This may contain HTML.""" id_field = '%s_id' % self.field_name # Look for this directly so that we don't end up fetching the # data for the object. if id_field in obj.__dict__: pk = obj.__dict__[id_field] if pk in state.data_cache: return state.data_cache[pk] else: value = getattr(obj, self.field_name) state.data_cache[pk] = escape(value) return value else: # Follow . separators like in the django template library value = obj for field_name in self.field_name.split('.'): if field_name: value = getattr(value, field_name) if six.callable(value): value = value() return escape(value)
def get_fieldsets(self, request, obj=None): """ Customize the fieldsets according to the app settings :param request: request :param obj: post :return: fieldsets configuration """ app_config_default = self._app_config_select(request, obj) if app_config_default is None and request.method == 'GET': return super(PostAdmin, self).get_fieldsets(request, obj) if not obj: config = app_config_default else: config = obj.app_config fsets = deepcopy(self._fieldsets) if config: if config.use_abstract: fsets[0][1]['fields'].append('abstract') if not config.use_placeholder: fsets[0][1]['fields'].append('post_text') else: if get_setting('USE_ABSTRACT'): fsets[0][1]['fields'].append('abstract') if not get_setting('USE_PLACEHOLDER'): fsets[0][1]['fields'].append('post_text') if get_setting('MULTISITE') and not self.has_restricted_sites(request): fsets[1][1]['fields'][0].append('sites') if request.user.is_superuser: fsets[1][1]['fields'][0].append('author') filter_function = get_setting('ADMIN_POST_FIELDSET_FILTER') if callable(filter_function): fsets = filter_function(fsets, request, obj=obj) return fsets
def render_cell(self, state, obj, render_context): """Renders the table cell containing column data.""" datagrid = state.datagrid rendered_data = self.render_data(state, obj) url = '' css_class = '' if self.link: try: url = self.link_func(state, obj, rendered_data) except AttributeError: pass if self.css_class: if six.callable(self.css_class): css_class = self.css_class(obj) else: css_class = self.css_class key = "%s:%s:%s:%s" % (state.last, rendered_data, url, css_class) if key not in state.cell_render_cache: ctx = Context(render_context) ctx.update({ 'column': self, 'column_state': state, 'css_class': css_class, 'url': url, 'data': mark_safe(rendered_data) }) state.cell_render_cache[key] = \ mark_safe(datagrid.cell_template_obj.render(ctx)) return state.cell_render_cache[key]
def get_context_data(self, name, value, attrs=None, bound_field=None): ctx = super(CropDusterWidget, self).get_context_data(name, value, attrs, bound_field) sizes = self.sizes related_object = ctx['instance'] preview_url = '' preview_w = PREVIEW_WIDTH preview_h = PREVIEW_HEIGHT if related_object: preview_url = related_object.get_image_url(size_name='_preview') orig_width, orig_height = related_object.width, related_object.height if (orig_width and orig_height): resize_ratio = min(PREVIEW_WIDTH / float(orig_width), PREVIEW_HEIGHT / float(orig_height)) if resize_ratio < 1: preview_w = int(round(orig_width * resize_ratio)) preview_h = int(round(orig_height * resize_ratio)) if six.callable(sizes): instance = getattr(getattr(bound_field, 'form', None), 'instance', None) try: sizes_callable = six.get_method_function(sizes) except AttributeError: sizes_callable = sizes sizes = sizes_callable(instance, related=related_object) sizes = [s for s in sizes if not getattr(s, 'is_alias', False)] ctx.update({ 'sizes': json.dumps(sizes), 'preview_url': preview_url, 'preview_w': preview_w, 'preview_h': preview_h, }) return ctx
def apply_django_settings(siteconfig, settings_map=None): """ Applies all settings from the site configuration to the Django settings object. """ if settings_map is None: settings_map = get_django_settings_map() for key, setting_data in six.iteritems(settings_map): if key in siteconfig.settings: value = siteconfig.get(key) setter = setattr if isinstance(setting_data, dict): setting_key = setting_data['key'] if 'setter' in setting_data: setter = setting_data['setter'] if ('deserialize_func' in setting_data and six.callable(setting_data['deserialize_func'])): value = setting_data['deserialize_func'](value) else: setting_key = setting_data setter(settings, setting_key, value)
def get_fieldsets(self, request, obj=None): """ Customize the fieldsets according to the app settings :param request: request :param obj: post :return: fieldsets configuration """ app_config_default = self._app_config_select(request, obj) if app_config_default is None and request.method == 'GET': return super(PostAdmin, self).get_fieldsets(request, obj) if not obj: config = app_config_default else: config = obj.app_config fsets = deepcopy(self._fieldsets) if config: if config.use_abstract: fsets[0][1]['fields'].append('abstract') if not config.use_placeholder: fsets[0][1]['fields'].append('post_text') else: if get_setting('USE_ABSTRACT'): fsets[0][1]['fields'].append('abstract') if not get_setting('USE_PLACEHOLDER'): fsets[0][1]['fields'].append('post_text') if get_setting('MULTISITE'): fsets[1][1]['fields'][0].append('sites') if request.user.is_superuser: fsets[1][1]['fields'][0].append('author') filter_function = get_setting('ADMIN_POST_FIELDSET_FILTER') if callable(filter_function): fsets = filter_function(fsets, request, obj=obj) return fsets
def get_fieldsets(self, request, obj=None): app_config_default = self._app_config_select(request, obj) if app_config_default is None and request.method == "GET": return super(PostAdmin, self).get_fieldsets(request, obj) if not obj: config = app_config_default else: config = obj.app_config fsets = deepcopy(self._fieldsets) if config: if config.use_abstract: fsets[0][1]["fields"].append("abstract") if not config.use_placeholder: fsets[0][1]["fields"].append("post_text") else: if get_setting("USE_ABSTRACT"): fsets[0][1]["fields"].append("abstract") if not get_setting("USE_PLACEHOLDER"): fsets[0][1]["fields"].append("post_text") if get_setting("MULTISITE"): fsets[1][1]["fields"][0].append("sites") if request.user.is_superuser: fsets[1][1]["fields"][0].append("author") filter_function = get_setting("ADMIN_POST_FIELDSET_FILTER") if callable(filter_function): fsets = filter_function(fsets, request, obj=obj) return fsets
def effective_default(self, field): """ Returns a field's effective database default value """ if field.has_default(): # 如果有默认值,则使用默认值 default = field.get_default() elif not field.null and field.blank and field.empty_strings_allowed: # 如果不允许为空,但是django运行不输入数据(对应的input就不回传服务器), 则使用空字符串 if field.get_internal_type() == "BinaryField": default = six.binary_type() else: default = six.text_type() else: default = None # If it's a callable, call it # default可以为函数(这个需要注意?) if six.callable(default): default = default() # Run it through the field's get_db_prep_save method so we can send it # to the database. default = field.get_db_prep_save(default, self.connection) return default
def get_link_serializer(self, field): """Return the function to use for serializing a link field.""" serialize_link_func = getattr(self, 'serialize_%s_link' % field, None) if not serialize_link_func or not six.callable(serialize_link_func): serialize_link_func = self.serialize_link return serialize_link_func
def _reinit(self, model_instance): """Re-initialize the value in the database from the initializer. Args: model_instance (django.db.models.Model): The model instance containing the field to re-initialize. """ if not (model_instance.pk or self._initializer or six.callable(self._initializer)): # We don't want to end up defaulting this to 0 if creating a # new instance unless an initializer is provided. Instead, # we'll want to handle this the next time the object is # accessed. return value = 0 if self._initializer: if isinstance(self._initializer, QueryExpressionType): value = self._initializer elif six.callable(self._initializer): model_instance_id = id(model_instance) self._locks[model_instance_id] = 1 value = self._initializer(model_instance) del self._locks[model_instance_id] if value is not None: is_expr = isinstance(value, QueryExpressionType) if is_expr and not model_instance.pk: value = 0 is_expr = False if is_expr: cls = model_instance.__class__ cls.objects.filter(pk=model_instance.pk).update( **{ self.attname: value, }) self._reload_model_instance(model_instance, [self.attname]) else: setattr(model_instance, self.attname, value) if model_instance.pk: model_instance.save(update_fields=[self.attname])
def scope_default_limit(self, scope, value=None): attr_name = '_default_quota_limit_%s' % self.name if value is not None: setattr(scope, attr_name, value) try: return getattr(scope, attr_name) except AttributeError: return self.default_limit(scope) if six.callable(self.default_limit) else self.default_limit
def _reinit(self, model_instance): """Re-initialize the value in the database from the initializer. Args: model_instance (django.db.models.Model): The model instance containing the field to re-initialize. """ if not (model_instance.pk or self._initializer or six.callable(self._initializer)): # We don't want to end up defaulting this to 0 if creating a # new instance unless an initializer is provided. Instead, # we'll want to handle this the next time the object is # accessed. return value = 0 if self._initializer: if isinstance(self._initializer, QueryExpressionType): value = self._initializer elif six.callable(self._initializer): model_instance_id = id(model_instance) self._locks[model_instance_id] = 1 value = self._initializer(model_instance) del self._locks[model_instance_id] if value is not None: is_expr = isinstance(value, QueryExpressionType) if is_expr and not model_instance.pk: value = 0 is_expr = False if is_expr: cls = model_instance.__class__ cls.objects.filter(pk=model_instance.pk).update(**{ self.attname: value, }) self._reload_model_instance(model_instance, [self.attname]) else: setattr(model_instance, self.attname, value) if model_instance.pk: model_instance.save(update_fields=[self.attname])
def get_jpeg_quality(width, height): if six.callable(CROPDUSTER_JPEG_QUALITY): return CROPDUSTER_JPEG_QUALITY(width, height) elif isinstance(CROPDUSTER_JPEG_QUALITY, (int, float)): return CROPDUSTER_JPEG_QUALITY else: raise ImproperlyConfigured( "CROPDUSTER_JPEG_QUALITY setting must be either a callable " "or a numeric value, got type %s" % (type(CROPDUSTER_JPEG_QUALITY).__name__))
def database_backwards(self, app_label, schema_editor, from_state, to_state): if self.reverse_code is None: raise NotImplementedError("You cannot reverse this operation") elif six.callable(self.reverse_code): self.reverse_code(models=from_state.render(), schema_editor=schema_editor) else: context = { "models": from_state.render(), "schema_editor": schema_editor, } eval(self.reverse_code, context)
def _reinit(model_instance): """Re-initializes the value in the database from the initializer.""" if not (model_instance.pk or self._initializer or six.callable(self._initializer)): # We don't want to end up defaulting this to 0 if creating a # new instance unless an initializer is provided. Instead, # we'll want to handle this the next time the object is # accessed. return if self._initializer and six.callable(self._initializer): self._locks[model_instance] = 1 value = self._initializer(model_instance) del self._locks[model_instance] else: value = 0 setattr(model_instance, self.attname, value) if model_instance.pk: model_instance.save()
def get_context_data(self, name, value, attrs=None, bound_field=None): ctx = super(CropDusterWidget, self).get_context_data(name, value, attrs, bound_field) sizes = self.sizes if six.callable(sizes): instance = getattr(getattr(bound_field, 'form', None), 'instance', None) related_object = ctx['instance'] sizes_callable = getattr(sizes, 'im_func', sizes) sizes = sizes_callable(instance, related=related_object) sizes = [s for s in sizes if not getattr(s, 'is_alias', False)] ctx.update({ 'sizes': json.dumps(sizes), }) return ctx
def database_forwards(self, app_label, schema_editor, from_state, to_state): # We now execute the Python code in a context that contains a 'models' # object, representing the versioned models as an AppCache. # We could try to override the global cache, but then people will still # use direct imports, so we go with a documentation approach instead. if six.callable(self.code): self.code(models=from_state.render(), schema_editor=schema_editor) else: context = { "models": from_state.render(), "schema_editor": schema_editor, } eval(self.code, context)
def effective_default(self, field): """ Returns a field's effective database default value """ if field.has_default(): default = field.get_default() elif not field.null and field.blank and field.empty_strings_allowed: default = "" else: default = None # If it's a callable, call it if callable(default): default = default() return default
def migrate_settings(siteconfig): """ Migrates any settings we want in the database from the settings file. """ # Convert everything in the table. for siteconfig_key, setting_data in six.iteritems(migration_table): if isinstance(setting_data, dict): setting_key = setting_data['key'] serialize_func = setting_data.get('serialize_func', None) else: setting_key = setting_data serialize_func = None default = defaults.get(siteconfig_key, None) value = getattr(settings, setting_key, default) if serialize_func and six.callable(serialize_func): value = serialize_func(value) siteconfig.set(siteconfig_key, value) # This may be a tuple in a tuple, or it may just be a tuple. if type(settings.ADMINS[0]) == tuple: admin = settings.ADMINS[0] else: admin = settings.ADMINS siteconfig.set('site_admin_name', admin[0]) siteconfig.set('site_admin_email', admin[1]) # Try to transform the authentication backend remaining_backends = [] known_backends = [] for auth_backend in settings.AUTHENTICATION_BACKENDS: if auth_backend in auth_backend_map: known_backends.append(auth_backend) else: remaining_backends.append(auth_backend) if remaining_backends or len(known_backends) > 1: # The user has some custom backend set. Just set the entire list siteconfig.set('auth_backend', 'custom') siteconfig.set('auth_custom_backends', settings.AUTHENTICATION_BACKENDS) elif len(known_backends) == 1: siteconfig.set('auth_backend', auth_backend_map[known_backends[0]]) else: siteconfig.set('auth_backend', 'builtin')
def render_cell(self, state, obj, render_context): """Renders the table cell containing column data.""" datagrid = state.datagrid try: rendered_data = self.render_data(state, obj) except Exception as e: logging.error('Error when calling render_data for DataGrid Column' ' %r: %s', self, e, exc_info=1) rendered_data = None if render_context: url = render_context.get('_datagrid_object_url') else: url = None css_class = '' if self.link: try: url = self.link_func(state, obj, rendered_data) except AttributeError: pass if self.css_class: if six.callable(self.css_class): css_class = self.css_class(obj) else: css_class = self.css_class key = "%s:%s:%s:%s" % (state.last, rendered_data, url, css_class) if key not in state.cell_render_cache: ctx = Context(render_context) ctx.update({ 'column': self, 'column_state': state, 'css_class': css_class, 'url': url, 'data': mark_safe(rendered_data) }) state.cell_render_cache[key] = \ mark_safe(datagrid.cell_template_obj.render(ctx)) return state.cell_render_cache[key]
def json_default(obj): if six.callable(getattr(obj, '__serialize__', None)): dct = obj.__serialize__() module = obj.__module__ if module == '__builtin__': module = None if isinstance(obj, type): name = obj.__name__ else: name = obj.__class__.__name__ type_name = u'.'.join(filter(None, [module, name])) if type_name == 'cropduster.resizing.Size': type_name = 'Size' dct.update({'__type__': type_name}) return dct raise TypeError("object of type %s is not JSON serializable" % type(obj).__name__)
def render_cell(self, state, obj, render_context): """Renders the table cell containing column data.""" datagrid = state.datagrid try: rendered_data = self.render_data(state, obj) except Exception as e: logging.error( 'Error when calling render_data for DataGrid Column' ' %r: %s', self, e, exc_info=1) rendered_data = None url = '' css_class = '' if self.link: try: url = self.link_func(state, obj, rendered_data) except AttributeError: pass if self.css_class: if six.callable(self.css_class): css_class = self.css_class(obj) else: css_class = self.css_class key = "%s:%s:%s:%s" % (state.last, rendered_data, url, css_class) if key not in state.cell_render_cache: ctx = Context(render_context) ctx.update({ 'column': self, 'column_state': state, 'css_class': css_class, 'url': url, 'data': mark_safe(rendered_data) }) state.cell_render_cache[key] = \ mark_safe(datagrid.cell_template_obj.render(ctx)) return state.cell_render_cache[key]
def effective_default(self, field): """ Returns a field's effective database default value """ if field.has_default(): default = field.get_default() elif not field.null and field.blank and field.empty_strings_allowed: default = "" else: default = None # If it's a callable, call it if callable(default): default = default() # Run it through the field's get_db_prep_save method so we can send it # to the database. default = field.get_db_prep_save(default, self.connection) return default
def get_fieldsets(self, request, obj=None): """ Customize the fieldsets according to the app settings :param request: request :param obj: post :return: fieldsets configuration """ app_config_default = self._app_config_select(request, obj) if app_config_default is None and request.method == 'GET': return super(PostAdmin, self).get_fieldsets(request, obj) if not obj: config = app_config_default else: config = obj.app_config fsets = deepcopy(self._fieldsets) if config: abstract = bool(config.use_abstract) placeholder = bool(config.use_placeholder) related = bool(config.use_related) else: abstract = get_setting('USE_ABSTRACT') placeholder = get_setting('USE_PLACEHOLDER') related = get_setting('USE_RELATED') if abstract: fsets[0][1]['fields'].append('abstract') if not placeholder: fsets[0][1]['fields'].append('post_text') if get_setting('MULTISITE') and not self.has_restricted_sites(request): fsets[1][1]['fields'][0].append('sites') if request.user.is_superuser: fsets[1][1]['fields'][0].append('author') if apps.is_installed('djangocms_blog.liveblog'): fsets[2][1]['fields'][2].append('enable_liveblog') filter_function = get_setting('ADMIN_POST_FIELDSET_FILTER') if related and Post.objects.namespace( config.namespace).active_translations().exists(): fsets[1][1]['fields'][0].append('related') if callable(filter_function): fsets = filter_function(fsets, request, obj=obj) return fsets
def get_modified_since(request, last_modified): """ Checks if a Last-Modified timestamp is newer than the requested HTTP_IF_MODIFIED_SINCE from the browser. This can be used to bail early if no updates have been performed since the last access to the page. This can take a DateField, datetime, HTTP date-formatted string, or a function for the last_modified timestamp. If a function is passed, it will only be called if the HTTP_IF_MODIFIED_SINCE header is present. """ if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE', None) if if_modified_since is not None: if six.callable(last_modified): last_modified = last_modified() return (if_modified_since == http_date(last_modified)) return False
def _normalize_value_field(self, value_field): """Normalize and return a value field. If ``value_field`` is a function, the resulting value field from calling that function will be returned. Otherwise, ``value_field`` will be returned directly. Args: value_field (object): A :py:class:`~djblets.conditions.values.BaseConditionValueField` or a function returning one. Returns: djblets.conditions.values.BaseConditionValueField: The resulting value field. """ if six.callable(value_field): value_field = value_field() return value_field
def get_fieldsets(self, request, obj=None): """ Customize the fieldsets according to the app settings :param request: request :param obj: post :return: fieldsets configuration """ app_config_default = self._app_config_select(request, obj) if app_config_default is None and request.method == 'GET': return super(PostAdmin, self).get_fieldsets(request, obj) if not obj: config = app_config_default else: config = obj.app_config fsets = deepcopy(self._fieldsets) if config: abstract = bool(config.use_abstract) placeholder = bool(config.use_placeholder) related = bool(config.use_related) else: abstract = get_setting('USE_ABSTRACT') placeholder = get_setting('USE_PLACEHOLDER') related = get_setting('USE_RELATED') if abstract: fsets[0][1]['fields'].append('abstract') if not placeholder: fsets[0][1]['fields'].append('post_text') if get_setting('MULTISITE') and not self.has_restricted_sites(request): fsets[1][1]['fields'][0].append('sites') if request.user.is_superuser: fsets[1][1]['fields'][0].append('author') if apps.is_installed('djangocms_blog.liveblog'): fsets[2][1]['fields'][2].append('enable_liveblog') filter_function = get_setting('ADMIN_POST_FIELDSET_FILTER') if related and Post.objects.namespace(config.namespace).active_translations().exists(): fsets[1][1]['fields'][0].append('related') if callable(filter_function): fsets = filter_function(fsets, request, obj=obj) return fsets
def apply_django_settings(siteconfig, settings_map=None): """Apply Django settings stored in the site configuration. This takes a siteconfiguration storing Django settings and a settings map, applying each of the settings to Django. Setting will generally be stored in the Django settings object, but some settings will be specially applied based on their rules in the settings map. Args: siteconfig (djblets.siteconfig.models.SiteConfiguration): The site configuration containing the Django settings to apply. settings_map (dict, optional): A map of siteconfig keys to Django settings information. See :py:func:`get_django_settings_map` for details. If not provided, the result of :py:func:`get_django_settings_map` will be used. """ if settings_map is None: settings_map = get_django_settings_map() for key, setting_data in six.iteritems(settings_map): if key in siteconfig.settings: value = siteconfig.get(key) setter = setattr if isinstance(setting_data, dict): setting_key = setting_data['key'] if 'setter' in setting_data: setter = setting_data['setter'] if ('deserialize_func' in setting_data and six.callable(setting_data['deserialize_func'])): value = setting_data['deserialize_func'](value) else: setting_key = setting_data setter(settings, setting_key, value)
def serialize_object(self, obj, *args, **kwargs): """Serializes the object, transforming text fields. This is a specialization of serialize_object that transforms any text fields that support text types. It also handles attaching the raw text to the payload, on request. """ data = super(MarkdownFieldsMixin, self).serialize_object( obj, *args, **kwargs) request = kwargs.get('request') if not request: force_text_type = None elif request.method == 'GET': force_text_type = request.GET.get('force-text-type') else: force_text_type = request.POST.get('force_text_type') if force_text_type not in self.TEXT_TYPES: force_text_type = None extra_text_type_fields = dict( (extra_text_type, {}) for extra_text_type in self._get_extra_text_types(obj, **kwargs) ) for field, field_info in six.iteritems(self.fields): if not field_info.get('supports_text_types'): continue get_func = getattr(self, 'get_is_%s_rich_text' % field, None) if six.callable(get_func): getter = lambda obj, *args: get_func(obj) else: getter = lambda obj, data, rich_text_field, text_type_field: \ getattr(obj, rich_text_field, None) self._serialize_text_info(obj, data, extra_text_type_fields, field, force_text_type, getter) if 'extra_data' in data: extra_data = data['extra_data'] all_text_types_extra_data = {} if obj.extra_data is None: obj.extra_data = {} # Work on a copy of extra_data, in case we change it. for field, value in six.iteritems(obj.extra_data.copy()): if not self.get_extra_data_field_supports_markdown(obj, field): continue # If all_text_types_extra_data is empty that implies we have # encountered the first field in extra_data which supports # markdown. In this case we must initialize the dictionary # with the extra text types that should be included in the # payload. if not all_text_types_extra_data: all_text_types_extra_data = dict( (k, {}) for k in six.iterkeys(extra_text_type_fields) ) # Note that we assume all custom fields are in Markdown by # default. This is to preserve compatibility with older # fields. New fields will always have the text_type flag # set to the proper value. self._serialize_text_info( obj, extra_data, all_text_types_extra_data, field, force_text_type, self._extra_data_rich_text_getter) for key, values in six.iteritems(all_text_types_extra_data): extra_text_type_fields[key]['extra_data'] = values for key, values in six.iteritems(extra_text_type_fields): data[key + '_text_fields'] = values return data
def __call__(self, request, api_format=None, *args, **kwargs): """Invokes the correct HTTP handler based on the type of request.""" if not hasattr(request, '_djblets_webapi_object_cache'): request._djblets_webapi_object_cache = {} auth_result = check_login(request) if isinstance(auth_result, tuple): auth_success, auth_message, auth_headers = auth_result if not auth_success: err = LOGIN_FAILED if auth_message: err = err.with_message(auth_message) return WebAPIResponseError( request, err=err, headers=auth_headers or {}, api_format=api_format, mimetype=self._build_error_mimetype(request)) method = request.method if method == 'POST': # Not all clients can do anything other than GET or POST. # So, in the case of POST, we allow overriding the method # used. method = request.POST.get('_method', kwargs.get('_method', method)) elif method == 'PUT': # Normalize the PUT data so we can get to it. # This is due to Django's treatment of PUT vs. POST. They claim # that PUT, unlike POST, is not necessarily represented as form # data, so they do not parse it. However, that gives us no clean # way of accessing the data. So we pretend it's POST for a second # in order to parse. # # This must be done only for legitimate PUT requests, not faked # ones using ?method=PUT. try: request.method = 'POST' request._load_post_and_files() request.method = 'PUT' except AttributeError: request.META['REQUEST_METHOD'] = 'POST' request._load_post_and_files() request.META['REQUEST_METHOD'] = 'PUT' request._djblets_webapi_method = method request._djblets_webapi_kwargs = kwargs request.PUT = request.POST if method in self.allowed_methods: if (method == "GET" and not self.singleton and (self.uri_object_key is None or self.uri_object_key not in kwargs)): view = self.get_list else: view = getattr(self, self.method_mapping.get(method, None)) else: view = None if view and six.callable(view): result = self.call_method_view(request, method, view, api_format=api_format, *args, **kwargs) if isinstance(result, WebAPIResponse): return result elif isinstance(result, WebAPIError): return WebAPIResponseError( request, err=result, api_format=api_format, mimetype=self._build_error_mimetype(request)) elif isinstance(result, tuple): headers = {} if method == 'GET': request_params = request.GET else: request_params = request.POST if len(result) == 3: headers = result[2] if 'Location' in headers: extra_querystr = '&'.join([ '%s=%s' % (param, request_params[param]) for param in SPECIAL_PARAMS if param in request_params ]) if extra_querystr: if '?' in headers['Location']: headers['Location'] += '&' + extra_querystr else: headers['Location'] += '?' + extra_querystr if isinstance(result[0], WebAPIError): return WebAPIResponseError( request, err=result[0], headers=headers, extra_params=result[1], api_format=api_format, mimetype=self._build_error_mimetype(request)) else: response_args = self.build_response_args(request) headers.update(response_args.pop('headers', {})) return WebAPIResponse(request, status=result[0], obj=result[1], headers=headers, api_format=api_format, encoder_kwargs=dict( { 'calling_resource': self, }, **kwargs), **response_args) elif isinstance(result, HttpResponse): return result else: raise AssertionError(result) else: return HttpResponseNotAllowed(self.allowed_methods)
def serialize_object(self, obj, *args, **kwargs): """Serializes the object into a Python dictionary.""" request = kwargs.get('request', None) if request: if not hasattr(request, '_djblets_webapi_serialize_cache'): request._djblets_webapi_serialize_cache = {} if obj in request._djblets_webapi_serialize_cache: return self._clone_serialized_object( request._djblets_webapi_serialize_cache[obj]) only_fields = self.get_only_fields(request) only_links = self.get_only_links(request) data = {} links = {} if only_links != []: links = self.get_links(self.item_child_resources, obj, *args, **kwargs) if hasattr(request, '_djblets_webapi_expanded_resources'): expanded_resources = request._djblets_webapi_expanded_resources else: expand = request.GET.get('expand', request.POST.get('expand', '')) expanded_resources = expand.split(',') request._djblets_webapi_expanded_resources = expanded_resources # Make a copy of the list of expanded resources. We'll be temporarily # removing items as we recurse down into any nested objects, to # prevent infinite loops. We'll want to make sure we don't # permanently remove these entries, or subsequent list items will # be affected. orig_expanded_resources = list(expanded_resources) for field in six.iterkeys(self.fields): can_include_field = only_fields is None or field in only_fields expand_field = field in expanded_resources # If we're limiting fields and this one isn't explicitly included, # then we're only going to want to process it if there's a chance # it'll be linked (as opposed to being expanded). if not can_include_field and expand_field: continue serialize_func = getattr(self, "serialize_%s_field" % field, None) if serialize_func and six.callable(serialize_func): value = serialize_func(obj, request=request) else: value = getattr(obj, field) if isinstance(value, models.Manager): if not can_include_field: # This field isn't a single Model, so it can't be # linked below. We can safely bail now before talking # to the database. continue value = value.all() elif isinstance(value, models.ForeignKey): value = value.get() # Make sure that any given field expansion only applies once. This # prevents infinite recursion in the case where there's a loop in # the object graph. # # We'll be restoring these values once we're done serializing # objects. if expand_field: request._djblets_webapi_expanded_resources.remove(field) if isinstance(value, models.Model) and not expand_field: serialize_link_func = self.get_link_serializer(field) links[field] = serialize_link_func(value, *args, **kwargs) elif can_include_field: if isinstance(value, QuerySet) and not expand_field: serialize_link_func = self.get_link_serializer(field) data[field] = [ serialize_link_func(o, *args, **kwargs) for o in value ] elif isinstance(value, QuerySet): objects = list(value) if objects: resource = self.get_serializer_for_object(objects[0]) data[field] = [ resource.serialize_object(o, *args, **kwargs) for o in objects ] else: data[field] = [] elif isinstance(value, models.Model): resource = self.get_serializer_for_object(value) data[field] = resource.serialize_object( value, *args, **kwargs) else: data[field] = value for resource_name in expanded_resources: if (resource_name not in links or (only_fields is not None and resource_name not in only_fields)): continue # Try to find the resource from the child list. found = False for resource in self.item_child_resources: if resource_name in [resource.name, resource.name_plural]: found = True break if not found or not resource.model: continue del links[resource_name] extra_kwargs = { self.uri_object_key: getattr(obj, self.model_object_key), } extra_kwargs.update(**kwargs) extra_kwargs.update(self.get_href_parent_ids(obj, **kwargs)) data[resource_name] = [ resource.serialize_object(o, *args, **kwargs) for o in resource._get_queryset(is_list=True, *args, **extra_kwargs) ] if only_links is None: data['links'] = links elif only_links != []: data['links'] = dict([ (link_name, link_info) for link_name, link_info in six.iteritems(links) if link_name in only_links ]) # Now that we're done serializing, restore the list of expanded # resource for the next call. request._djblets_webapi_expanded_resources = orig_expanded_resources if request: request._djblets_webapi_serialize_cache[obj] = \ self._clone_serialized_object(data) return data
def serialize_object(self, obj, *args, **kwargs): """Serializes the object into a Python dictionary.""" request = kwargs.get('request', None) if request: if not hasattr(request, '_djblets_webapi_serialize_cache'): request._djblets_webapi_serialize_cache = {} if obj in request._djblets_webapi_serialize_cache: return self._clone_serialized_object( request._djblets_webapi_serialize_cache[obj]) only_fields = self.get_only_fields(request) only_links = self.get_only_links(request) data = {} links = {} if only_links != []: links = self.get_links(self.item_child_resources, obj, *args, **kwargs) if hasattr(request, '_djblets_webapi_expanded_resources'): expanded_resources = request._djblets_webapi_expanded_resources else: expand = request.GET.get('expand', request.POST.get('expand', '')) expanded_resources = expand.split(',') request._djblets_webapi_expanded_resources = expanded_resources # Make a copy of the list of expanded resources. We'll be temporarily # removing items as we recurse down into any nested objects, to # prevent infinite loops. We'll want to make sure we don't # permanently remove these entries, or subsequent list items will # be affected. orig_expanded_resources = list(expanded_resources) for field in six.iterkeys(self.fields): can_include_field = only_fields is None or field in only_fields expand_field = field in expanded_resources # If we're limiting fields and this one isn't explicitly included, # then we're only going to want to process it if there's a chance # it'll be linked (as opposed to being expanded). if not can_include_field and expand_field: continue serialize_func = getattr(self, "serialize_%s_field" % field, None) if serialize_func and six.callable(serialize_func): value = serialize_func(obj, request=request) else: value = getattr(obj, field) if isinstance(value, models.Manager): if not can_include_field: # This field isn't a single Model, so it can't be # linked below. We can safely bail now before talking # to the database. continue value = value.all() elif isinstance(value, models.ForeignKey): value = value.get() # Make sure that any given field expansion only applies once. This # prevents infinite recursion in the case where there's a loop in # the object graph. # # We'll be restoring these values once we're done serializing # objects. if expand_field: request._djblets_webapi_expanded_resources.remove(field) if isinstance(value, models.Model) and not expand_field: serialize_link_func = self.get_link_serializer(field) links[field] = serialize_link_func(value, *args, **kwargs) elif can_include_field: if isinstance(value, QuerySet) and not expand_field: serialize_link_func = self.get_link_serializer(field) data[field] = [ serialize_link_func(o, *args, **kwargs) for o in value ] elif isinstance(value, QuerySet): objects = list(value) if objects: resource = self.get_serializer_for_object(objects[0]) data[field] = [ resource.serialize_object(o, *args, **kwargs) for o in objects ] else: data[field] = [] elif isinstance(value, models.Model): resource = self.get_serializer_for_object(value) data[field] = resource.serialize_object( value, *args, **kwargs) else: data[field] = value for resource_name in expanded_resources: if (resource_name not in links or (only_fields is not None and resource_name not in only_fields)): continue # Try to find the resource from the child list. found = False for resource in self.item_child_resources: if resource_name in [resource.name, resource.name_plural]: found = True break if not found or not resource.model: continue del links[resource_name] extra_kwargs = { self.uri_object_key: getattr(obj, self.model_object_key), } extra_kwargs.update(**kwargs) extra_kwargs.update(self.get_href_parent_ids(obj, **kwargs)) data[resource_name] = [ resource.serialize_object(o, *args, **kwargs) for o in resource._get_queryset( is_list=True, *args, **extra_kwargs) ] if only_links is None: data['links'] = links elif only_links != []: data['links'] = dict([ (link_name, link_info) for link_name, link_info in six.iteritems(links) if link_name in only_links ]) # Now that we're done serializing, restore the list of expanded # resource for the next call. request._djblets_webapi_expanded_resources = orig_expanded_resources if request: request._djblets_webapi_serialize_cache[obj] = \ self._clone_serialized_object(data) return data
def sizes(self): if six.callable(self.field.db_field.sizes): return self.field.db_field.sizes(self.instance, related=self.related_object) else: return self.field.db_field.sizes
def __call__(self, request, api_format=None, *args, **kwargs): """Invokes the correct HTTP handler based on the type of request.""" if not hasattr(request, '_djblets_webapi_object_cache'): request._djblets_webapi_object_cache = {} auth_result = check_login(request) if isinstance(auth_result, tuple): auth_success, auth_message, auth_headers = auth_result if not auth_success: err = LOGIN_FAILED if auth_message: err = err.with_message(auth_message) return WebAPIResponseError( request, err=err, headers=auth_headers or {}, api_format=api_format, mimetype=self._build_error_mimetype(request)) method = request.method if method == 'POST': # Not all clients can do anything other than GET or POST. # So, in the case of POST, we allow overriding the method # used. method = request.POST.get('_method', kwargs.get('_method', method)) elif method == 'PUT': # Normalize the PUT data so we can get to it. # This is due to Django's treatment of PUT vs. POST. They claim # that PUT, unlike POST, is not necessarily represented as form # data, so they do not parse it. However, that gives us no clean # way of accessing the data. So we pretend it's POST for a second # in order to parse. # # This must be done only for legitimate PUT requests, not faked # ones using ?method=PUT. try: request.method = 'POST' request._load_post_and_files() request.method = 'PUT' except AttributeError: request.META['REQUEST_METHOD'] = 'POST' request._load_post_and_files() request.META['REQUEST_METHOD'] = 'PUT' request._djblets_webapi_method = method request._djblets_webapi_kwargs = kwargs request.PUT = request.POST if method in self.allowed_methods: if (method == "GET" and not self.singleton and (self.uri_object_key is None or self.uri_object_key not in kwargs)): view = self.get_list else: view = getattr(self, self.method_mapping.get(method, None)) else: view = None if view and six.callable(view): result = self.call_method_view( request, method, view, api_format=api_format, *args, **kwargs) if isinstance(result, WebAPIResponse): return result elif isinstance(result, WebAPIError): return WebAPIResponseError( request, err=result, api_format=api_format, mimetype=self._build_error_mimetype(request)) elif isinstance(result, tuple): headers = {} if method == 'GET': request_params = request.GET else: request_params = request.POST if len(result) == 3: headers = result[2] if 'Location' in headers: extra_querystr = '&'.join([ '%s=%s' % (param, request_params[param]) for param in SPECIAL_PARAMS if param in request_params ]) if extra_querystr: if '?' in headers['Location']: headers['Location'] += '&' + extra_querystr else: headers['Location'] += '?' + extra_querystr if isinstance(result[0], WebAPIError): return WebAPIResponseError( request, err=result[0], headers=headers, extra_params=result[1], api_format=api_format, mimetype=self._build_error_mimetype(request)) else: response_args = self.build_response_args(request) headers.update(response_args.pop('headers', {})) return WebAPIResponse( request, status=result[0], obj=result[1], headers=headers, api_format=api_format, encoder_kwargs=dict({ 'calling_resource': self, }, **kwargs), **response_args) elif isinstance(result, HttpResponse): return result else: raise AssertionError(result) else: return HttpResponseNotAllowed(self.allowed_methods)
import hashlib
def serialize_object(self, obj, *args, **kwargs): """Serializes the object, transforming text fields. This is a specialization of serialize_object that transforms any text fields that support text types. It also handles attaching the raw text to the payload, on request. """ data = super(MarkdownFieldsMixin, self).serialize_object( obj, *args, **kwargs) request = kwargs.get('request') if not request: force_text_type = None elif request.method == 'GET': force_text_type = request.GET.get('force-text-type') else: force_text_type = request.POST.get('force_text_type') if force_text_type not in self.TEXT_TYPES: force_text_type = None extra_text_type_fields = dict( (extra_text_type, {}) for extra_text_type in self._get_extra_text_types(obj, **kwargs) ) for field, field_info in six.iteritems(self.fields): if not field_info.get('supports_text_types'): continue get_func = getattr(self, 'get_is_%s_rich_text' % field, None) if six.callable(get_func): getter = lambda obj, *args: get_func(obj) else: getter = lambda obj, data, rich_text_field, text_type_field: \ getattr(obj, rich_text_field, None) self._serialize_text_info(obj, data, extra_text_type_fields, field, force_text_type, getter) if 'extra_data' in data: extra_data = data['extra_data'] all_text_types_extra_data = {} # Work on a copy of extra_data, in case we change it. for field, value in six.iteritems(obj.extra_data.copy()): if not self.get_extra_data_field_supports_markdown(obj, field): continue # If all_text_types_extra_data is empty that implies we have # encountered the first field in extra_data which supports # markdown. In this case we must initialize the dictionary # with the extra text types that should be included in the # payload. if not all_text_types_extra_data: all_text_types_extra_data = dict( (k, {}) for k in six.iterkeys(extra_text_type_fields) ) # Note that we assume all custom fields are in Markdown by # default. This is to preserve compatibility with older # fields. New fields will always have the text_type flag # set to the proper value. self._serialize_text_info( obj, extra_data, all_text_types_extra_data, field, force_text_type, self._extra_data_rich_text_getter) for key, values in six.iteritems(all_text_types_extra_data): extra_text_type_fields[key]['extra_data'] = values for key, values in six.iteritems(extra_text_type_fields): data[key + '_text_fields'] = values return data
def target_models(self): if not hasattr(self, '_target_models'): self._target_models = (self._raw_target_models() if six.callable( self._raw_target_models) else self._raw_target_models) return self._target_models