Adding record of page views and details of change to django_admin_log - django

We are using LogEntry/django_admin_log and recording additions, changes and deletions made from Django Admin. However we have two issues we need to address:
1.) Changes record only the field that changed. Not the old and new values. We would like to add the specific details of all changes.
2.) We would like to record an action in this log every time a page is viewed on the Django Admin panel.
How would it be best to proceed?
We are happy to do some work to extend the existing functionality of this, or we are happy to move to a completely new 3rd part module if necessary, or write our own. But would like some guidance from some experts?

We had similar requirements in terms of keeping history and track of actions done by users with a higher level of detail in terms of values changes. What we ended up doing was creating a new model called "History" which we populated with the user, the name of the model being changed, the instance id and a dictionary called changes showing the name of each field changed and values from - to.
In order to populate the new model, we overrode the save_model function in the admin file for the model we want to track. Regarding the page views, you can overrride the get_fields if "obj" is not None and create an instance of History accordingly.

Related

How to implement the add/edit links on a field in the admin change page that does not have an expressed FK relationship in the model?

I have a Django 3.x web site. There are 3 models, among others, Document, MetaData, MetaDataValue. The MetaDataValue model has a foreign key relationship to the MetaData model.
In a simple world, there would be a foreign key relationship from MetaData to Document. Then the DocumentAdmin would display the MetaData field as a dropdown, filled with the appropriate MetaDataValues. There would also be edit/add (pencil/plus) links next to the dropdown to allow the user to edit/add the MetaDataValue for that MetaData object.
However, one of the requirements for the project was not to define which, or how many, MetaData objects would be associated with a particular Document type until run time. Those associations are through another set of models. Therefore, the Document admin change page adds the different MetaData fields to the change admin page at run time, by looking into the database for which MetaData fields are associated with this type of Document, and then adding them to the fieldset through the get_fieldsets method.
The implication of this design is that one cannot edit/add values to a particular MetaData field on the Document admin change page, because, I assume, Django lost the underlying foreign key relationship between the Document and MetaData models because the fieldset is generated in the admin's get_fieldsets method.
I could add pages of code to show how the MetaData fields are generated at run time, but I don't think that would make the description any clearer. I have looked at the page source and fieldsets generated for the "simple world" example described above above, and cannot see where Django is figuring out when to add the edit/change links to the drop down for a particular foreign key field.
My question, is how can I add the pencil and plus sign to these MetaData fields displayed in the Document admin change page, and give the user to option to add/edit the MetaDataValue for that particular MetaData object? I could just create some Ajax calls and do all the heavy lifting myself, bu I would prefer to leverage as much of the Django infrastructure as possible, and not reinvent more than I have to.
Thanks!
Mark
I found a solution using the RelatedFieldWidgetWrapper. I added this widget wrapper to all my selects that needed the green plus (add) and yellow pencil (edit).
There is too much code to post here, but here is the meat of it. My db table MetaData has the names of all the fields, and MetaDataValue has the values for each metadata field. The metadata is used to describe a document (text, video, image), but there are no FK relationships between the MetaData model and the Document model. The relationship between the metadata and the documents is contained in a JSON field in another table. The MetaData table also has what type of Django field is used for that metadata field. There is also another table that determines which metadata fields apply to which document type (image, text, video). For all the ModelChoiceFields, I add the field and the widget (Select wrapped in a RelatedFieldWidgetWrapper).
elif (metadata_names[i].field_type == MetaData.MODELCHOICEFIELD):
fields[metadata_names[i].name] = forms.ModelChoiceField(queryset=MetaDataValue.objects.filter(metadata_id=metadata_names[i].metadata_id).order_by('value'), required=False, label=metadata_names[i].label, help_text=metadata_names[i].help_text)
# add the green plus (add) and pencil (edit) links to each select field
fields[metadata_names[i].name].widget = RelatedFieldWidgetWrapper(fields[metadata_names[i].name].widget, MetaDataValue._meta.get_field('tag_value'), admin_site, can_add_related=True, can_change_related=True)
if 'documentType_id' in fields:
# Editing/add a new document type seems like a bad idea, as document type is used a lot in the processing logic
fields['documentType_id'].widget.can_add_related = False
fields['documentType_id'].widget.can_change_related = False
The hard part was getting the right arguments for the RelatedFieldWidgetWrapper. The documentation is a little sparse on that widget, because, I think, it was an early part of the admin, but much of the Django code has changed since it was first implemented, so a lot of ways this wrapper was used in the past (ie stack overflow posts) has been deprecated. I just looked at the source, and tried what seemed to be appropriate until it worked. Finally, these articles helped a lot:
RelatedFieldWidgetWrapper
More RelatedFieldWidgetWrapper – My Very Own Popup
How can I remove the add and change buttons from a TabularInline admin field?

Django Admin LogEntry: how it works in non admin actions?

I am having some struggles how does exactly django.admin.LogEntry objects are created.
Consider the following scenario:
I have a bunch of functions which take a csv file with data that allow me to create multiple objects at one call (just iterate through the file, use the data and if data in given row is correct: create a Model instance). I want to make sure that that each of that creation will be logged.
The question is: django docs are not very descriptive on how does LogEntry works and I am not sure if such actions (not taken in the admin panel itself) will be logged there. Also: will the LogEntries be created for the related objects or I have to trigger them manually?
Does anybody got any experience with such scenarios and can share thoughts about it?
The LogEntry model is in the Admin package and only used by Django admin by default. It is used in the admin layer and not model layer when saving objects. if you want to use it outside the admin, then you will have to manually create the entries yourself. That also means the admin will likely display entries of changes made by normal users so you have to think about how you want the entries displayed

Adding a new section in Mezzanine CMS (Django) admin

I'm totally new to Mezzanine CMS. I got handed a site to work with and so far I've been able to do all the changes without problem. I've run across a problem in which they want a new section in the home page. I go to the admin section to edit the home page, but there is no extra content field.
On the home page, I see 4 sections "content" "priorities" "testimonials" and "clients". I would like to have another "content" area as a 5th section. How do I go on and add this section? I'm totally new to Django but would be appreciative if someone could explain or point in the right direction.
Here is a link to an image for reference.
https://imgur.com/a/sUKOtvS
Thanks in advance
The homepage content would be backed by a Django model with attributes for the partners and testimonial fields. You'll need to find the Python class for this model in your code base (you could search for those field names), and you'll need to add a new attribute for the new section you need.
Django and Mezzanine have lots of different field types you can use for these attributes, so consult their respective documentation for how those work (Django's are a lot more comprehensive, so start there).
Once you've done that, you'll need to create a database migration for the new attribute - that adds the field to the database table that will store the actual content, again consult the Django documentation for how these work.
Finally you may need to add the new field to the Admin class, which is the Python class (similar to the model) that controls which fields appear in the admin interface, and how they appear. I say "may" as these generally appear automatically without any code, but if things have been modified to a certain extent, you may need to do this manually.

Custom [admin] interfaces with Django?

I've been playing around with python/django for the last couple of weeks and whilst the overall structure and makeup of the framework is making sense I'm rather confused on how to create advanced interfaces (in relation to tasks administrators would perform). One trivial example I'm playing around with at the moment is a bulk csv product import for different suppliers which will update various fields of a particular product (keeping track of any changes), creating items where they don't already exist and applying other business logic etc.
With the data successfully in the database and the models reflecting this I envisage a view whereby one could select a supplier from a drop down, which would load all the products silently in the background and display a datagrid on success. The user could then interact with each product individually, for example selecting would display a stacked line chart of pricing history above the datagrid and an optional fly-in panel to the right with options to update prices, add notes etc.
Are there any best practice approaches for achieving something along these lines, does one create custom views/templates or put some heavy lifting into overriding the default Django admin functionality?
Any help is appreciated, thanks in advance.
You can either:
Create a custom django admin action that will appear as an option in the changelist page dropdown menu (of the Supplier model for example.) You can then apply this action to the selected rows. You can also have intermediate pages when using admin actions
You can hook your own views into the django admin for particular models and and then overwrite the appropriate django admin templates to link the two together

django: Hide Model Types with no instances on Admin page

Say I have an app with ModelA, ModelB, and ModelC
For my app's admin page (/admin/app), how do I hook into the display of the "App administration" page so I may hide Model types that are empty (have no instances)?
That's a big ask, because you'd have to dynamically register/unregister apps according to the results of a database/ORM query (a count(), at least) for each of the models that each of the INSTALLED_APPS contains.
Every single time you viewed the admin.
While that in itself is unpleasant enough, bear in mind that admin.py for each app is processed at server restart/reload time (IIRC), so you could't hope to hook up something cute like those DB lookups there, as it'd only get run once and not reflect the actual state of models currently have objects stored in your database.
Better move: leave it be. If there are no objects to view for a given model, then there are no objects to view for a given model.
If you're concerned about your client/user making new models in there when they shouldn't, then that's a combination of user education and admin user permissions that you can sort out.
As said in a few places: "The Admin is not your app." If the customization goes beyond the trivial, it's time to write your own views. You can still hook them in to the admin site by overriding the base admin template and even serving them from the same root path as the rest of your admin.