Usage¶
It’s attached to a model via a field that during django setup phase creates a model related to that specific parent model.
EditSuggestion instances: - can be modified/deleted by the author of each instance - status can be “under review”, “rejected” and “published” - status change need to pass a condition - changing the status to “published” updates the tracked model and locks the edit suggestion from being edited/deleted
Parent Model Example¶
Model has a field “edit_suggestion” that instantiates EditSuggestion A serializer module and parent serializer is passed as a tuple ex:
class Tag(models.Model)
name = models.CharField(max_length=126)
def condition_check(user, parent_model_instance, edit_suggestion_instance):
# do some checks and return a boolean
if user.is_superuser or parent_model_instance.author == user:
return True
return False
class ParentModel(models.Model):
excluded_field = models.IntegerField()
m2m_type_field = models.ManyToManyField(Tags)
edit_suggestions = EditSuggestion(
excluded_fields=['excluded_field'],
m2m_fields=({
'name': 'm2m_type_field',
'model': Tag,
'through': 'optional. empty if not used',
}),
change_status_condition=condition_check,
bases=(VotableMixin,), # optional. bases are used to build the edit suggestion model upon them
user_class=CustomUser, # optional. uses the default user model
)
At django initializing stage the Edit Suggestion App creates a model for each Model having this field ex: “EditSuggestionParentModel”
Can access the model by ParentModel.edit_suggestions.model
How to use¶
Create new edit suggestion¶
After setting up the field inside the parent model just create a new edit suggestion by invoking the model new()
method:
edit_suggestion = parentModelInstance.edit_suggestions.new({
**edit_data,
'edit_suggestion_author': user_instance
})
Diff against the parent¶
Can see the differences between the parent instance and the curent edit:
changes = edit_suggestion.diff_against_parent()
It will return an object ModelDelta
that has the attributes:
- object.changes: tracked changes
- object.changed_fields: changed fields name
- object.old_record: parent instance
- object.new_record: current edit instance
Publish¶
To publish an edit suggestion you need to pass in an user. If the change_status_condition
does not pass,
a django.contrib.auth.models.PermissionDenied
exception will be raised.
edit_suggestion.edit_suggestion_publish(user)
This will change the status from edit_suggestion.Status.UNDER_REVIEWS
to edit_suggestion.Status.PUBLISHED
.
After publishing, the edit suggestion won’t be able to be edited anymore.
Reject¶
To reject an edit suggestion you need to pass in an user and a reason. If the change_status_condition
does not pass,
a django.contrib.auth.models.PermissionDenied
exception will be raised.
edit_suggestion.edit_suggestion_reject(user, reason)
This will change the status from edit_suggestion.Status.UNDER_REVIEWS
to edit_suggestion.Status.REJECTED
.
After rejecting, the edit suggestion won’t be able to be edited anymore.
Foreign Fields different than type ForeignField¶
If using a foreign field different than ForeignField, like mptt.fields.TreeForeignKey
use argument special_foreign_fields
when initializing the EditSuggestion:
edit_suggestions = EditSuggestion(
excluded_fields=(
'created_at', 'updated_at', 'author', 'thumbs_up_array', 'thumbs_down_array'),
special_foreign_fields=['parent',],
change_status_condition=edit_suggestion_change_status_condition,
post_publish=post_publish_edit,
post_reject=post_reject_edit
)
M2M Fields¶
Can add ManyToManyField references by passing actual model or string. For referencing self instance use 'self'
:
class M2MSelfModel(models.Model):
name = models.CharField(max_length=64)
children = models.ManyToManyField('M2MSelfModel')
edit_suggestions = EditSuggestion(
m2m_fields=(({
'name': 'children',
'model': 'self',
},)),
change_status_condition=condition_check,
)
M2M Through support¶
Can use ManyToManyField with through
table. The original pivot table will get copied and modified to point to the edit suggestion model.
To save/edit the edit suggestion with m2m through
field need to use a custom method.
class SharedChild(models.Model):
name = models.CharField(max_length=64)
def __str__(self):
return self.name
class SharedChildOrder(models.Model):
parent = models.ForeignKey('ParentM2MThroughModel', on_delete=models.CASCADE)
shared_child = models.ForeignKey(SharedChild, on_delete=models.CASCADE)
order = models.IntegerField(default=0)
class ParentM2MThroughModel(models.Model):
name = models.CharField(max_length=64)
children = models.ManyToManyField(SharedChild, through=SharedChildOrder)
edit_suggestions = EditSuggestion(
m2m_fields=(({
'name': 'children',
'model': SharedChild,
'through': {
'model': SharedChildOrder,
'self_field': 'parent',
},
},)),
change_status_condition=condition_check,
bases=(VotableMixin,), # optional. bases are used to build the edit suggestion model upon them
user_model=User, # optional. uses the default user model
)
Django REST integration¶
In 1.23 comes with EditSuggestionSerializer and ModelViewsetWithEditSuggestion.
There are 2 serializers: the one for listing (with minimal informations) and the one for detail/form view with all info.
The serializer is used for supplying the method get_edit_suggestion_serializer
to the serializer for the model that receives edit suggestions.
This method should return the edit suggestion serializer.
The serializer is used for supplying the method get_edit_suggestion_listing_serializer
to the serializer for the model that receives edit suggestions.
This method should return the edit suggestion serializer.
class TagSerializer(ModelSerializer):
queryset = Tag.objects
class Meta:
model = Tag
fields = ['name', ]
class ParentEditListingSerializer(ModelSerializer):
queryset = ParentModel.edit_suggestions
class Meta:
model = ParentModel.edit_suggestions.model
fields = ['pk', 'edit_suggestion_reason', 'edit_suggestion_author', 'edit_suggestion_date_created']
class ParentEditSerializer(ModelSerializer):
queryset = ParentModel.edit_suggestions
tags = TagSerializer(many=True)
class Meta:
model = ParentModel.edit_suggestions.model
fields = ['name', 'tags', 'edit_suggestion_reason', 'edit_suggestion_author']
class ParentSerializer(EditSuggestionSerializer):
queryset = ParentModel.objects
tags = TagSerializer(many=True)
class Meta:
model = ParentModel
fields = ['name', 'tags']
@staticmethod
def get_edit_suggestion_serializer():
return ParentEditSerializer
@staticmethod
def get_edit_suggestion_listing_serializer():
return ParentEditListingSerializer
The ModelViewsetWithEditSuggestion is to be inherited from when creating the model viewset:
class ParentViewset(ModelViewsetWithEditSuggestion):
serializer_class = ParentSerializer
queryset = ParentSerializer.queryset
It will add edit_suggestions
for GET and create_edit_suggestion
for POST requests.
Have edit_suggestion_publish
and edit_suggestion_reject
for POST requests.
Thus, to retrieve the edit suggestions for a specific resource using django rest we would send
a GET request to reverse('parent-viewset-edit-suggestions', kwargs={'pk': 1})
.
The url in string form would be /api/parent/1/create_edit_suggestion/
.
- To create an edit suggestion for a resource there are 2 ways:
1. POST request to
reverse('parent-viewset-create-edit-suggestion', kwargs={'pk': 1})
The url in string form would be/api/parent/1/edit_suggestions/
.2. use
ModelViewsetWithEditSuggestion
methodedit_suggestion_perform_create
since 1.34 the foreign key fields are handled as well
To publish using the viewset send a POST request to reverse('parent-viewset-edit-suggestion-publish', kwargs={'pk': 1})
with a json object having edit_suggestion_id
key with the edit suggestion pk.
To reject using the viewset send a POST request to reverse('parent-viewset-edit-suggestion-reject', kwargs={'pk': 1})
with a json object having edit_suggestion_id
key with the edit suggestion pk and edit_suggestion_reject_reason
as the reason for rejection.
The responses will return status 403 if the rule does not verify, 401 for another exception and 200 for success.
Django REST integration for m2m through
¶
In 1.30 we can handle creating edit suggestions with through m2m fields. It’s the same procedure as with creating a normal edit suggestion but for the through m2m data we are using this data structure in the POST:
The creation is handled by the edit_suggestion_handle_m2m_through_field
method of ModelViewsetWithEditSuggestion
viewset.
If there is a need to handle this in a different way, just override the method in your viewset.