def prepare_cloned_fields(instance): """ Compile an object's `clone_fields` list into a string of URL query parameters. Tags are automatically cloned where applicable. """ params = [] for field_name in getattr(instance, "clone_fields", []): field = instance._meta.get_field(field_name) field_value = field.value_from_object(instance) # Swap out False with URL-friendly value if field_value is False: field_value = "" # This is likely an m2m field if isinstance(field_value, list): for fv in field_value: item_value = getattr(fv, "pk", str(fv)) # pk or str() params.append((field_name, item_value)) # Omit empty values elif field_value not in (None, ""): params.append((field_name, field_value)) # Copy tags if is_taggable(instance): for tag in instance.tags.all(): params.append(("tags", tag.pk)) # Concatenate parameters into a URL query string param_string = "&".join([f"{k}={v}" for k, v in params]) return param_string
def serialize_object(obj, extra=None, exclude=None): """ Return a generic JSON representation of an object using Django's built-in serializer. (This is used for things like change logging, not the REST API.) Optionally include a dictionary to supplement the object data. A list of keys can be provided to exclude them from the returned dictionary. Private fields (prefaced with an underscore) are implicitly excluded. """ json_str = serialize("json", [obj]) data = json.loads(json_str)[0]["fields"] # Include custom_field_data as "custom_fields" if hasattr(obj, "custom_field_data"): data["custom_fields"] = data.pop("custom_field_data") # Include any tags. Check for tags cached on the instance; fall back to using the manager. if is_taggable(obj): tags = getattr(obj, "_tags", obj.tags.all()) data["tags"] = [tag.name for tag in tags] # Append any extra data if extra is not None: data.update(extra) # Copy keys to list to avoid 'dictionary changed size during iteration' exception for key in list(data): # Private fields shouldn't be logged in the object change if isinstance(key, str) and key.startswith("_"): data.pop(key) # Explicitly excluded keys if isinstance(exclude, (list, tuple)) and key in exclude: data.pop(key) return data