Sort objects in Django admin by a model #property? - python

I have two models - a Task model, and a Worker model. I have a property on Worker that counts how many tasks they have completed this month.
class Task(models.Model):
# ...
completed_on = models.DateField()
class Worker(models.Model):
# ...
#property
def completed_this_month(self):
year = datetime.date.today().year
month = datetime.date.today().month
return Task.objects.filter(worker=self,
completed_on__year=year,
completed_on__month=month).count()
I've added this field to the Worker admin, and it displays correctly.
I would like to be able to sort by this field. Is there a way to do this?
Edit: It has been suggested that my question is a duplicate of this question, which uses extra(). The Django documentation strongly advises against using the extra() method, and even asks you to file a ticket explaining why you had to use it.
Use this method as a last resort
This is an old API that we aim to deprecate at some point in the future. Use it only if you cannot express your query using other queryset methods. If you do need to use it, please file a ticket using the QuerySet.extra keyword with your use case (please check the list of existing tickets first) so that we can enhance the QuerySet API to allow removing extra().

Related

ModelManager used in ForeignKey relations

I'm using Django 1.9 on my site and I need an effective way of completely ignore the inactive users, so I don't send them any notifications, emails, etc.
I've tried using a custom Model Manager that only returns the active ones, like this:
class ActiveAccountsManager(models.Manager):
use_for_related_fields = True
def get_queryset(self):
return super(ActiveAccountsManager, self).get_queryset().filter(user__is_active=True)
class Account(models.Model):
class Meta:
verbose_name = _('Account')
verbose_name_plural = _('Accounts')
objects = ActiveAccountsManager()
all_accounts = models.Manager() # Enabling the obtention of all the users, instead of only the active ones
user = models.OneToOneField(User)
type = models.IntegerField(choices=ACCOUNT_TYPES, default=-1)
And, while it works exactly as I want when I directly try to query Account objects, it doesn't when they are referenced through an object that has a ForeignKey relation with it. For example, if I had a Comment model like the following one:
class Comment(models.Model):
author = models.ForeignKey(Account)
I'd like that, when I query for Comment objects, the ones whose author is a inactive user (i.e. an user that the default Manager of the Account model won't return) aren't returned either, instead of the current behavior that returns the comment but says that the account related to it does not exist
Is there any way of achieving this without specifically defining a custom ModelManager on the Comment class? Because the thing is that I want to achieve this in a lot of classes related to the Account one and I can't think of a way of doing it without being extremely repetitive.
Please, don't hesitate to ask if any part of my question isn't clear enough or if I need to provide more details about my code. Thank you so much in advance.
It should already work in your case, as the docs (https://docs.djangoproject.com/en/1.9/topics/db/managers/#default-managers) say:
Default managers
If you use custom Manager objects, take note that the first Manager
Django encounters (in the order in which they’re defined in the model)
has a special status. Django interprets the first Manager defined in a
class as the “default” Manager, and several parts of Django (including
dumpdata) will use that Manager exclusively for that model. As a
result, it’s a good idea to be careful in your choice of default
manager in order to avoid a situation where overriding get_queryset()
results in an inability to retrieve objects you’d like to work with.
Since the ActiveAccountsManager declaration comes first in your model, it should be already marked as the "default" one.
If you were to update to Django >= 1.10, you can set up explicitly the default manager for your model (see https://docs.djangoproject.com/en/1.11/ref/models/options/#default-manager-name).

Django set privacy options per model field

I have gone through the question, best way to implement privacy on each field in model django and Its answers doesn't seem solve my problem so I am asking some what related question here,
well, I have a User model. I want the user to make possible to control the privacy of each and every field of their profile (may be gender, education, interests etc . ..).
The privacy options must not to be limited to just private or public, but as descriptive as
public
friends
only me
friend List 1 (User.friendlist.one)
friend List 2 (User.friendlist.two)
friend List 3 (User.friendlist.three)
another infinte lists that user may create.
I also don't want these privacy options to be saved on another model, but the same so that with one query I could get the user object along with the privacy options.
so If I have the UserModel,
class User(models.Model):
name = models.CharField()
email = models.EmailField()
phone = models.CharField()
How do I setup a privacy setting here? I am using postgres, can I map a JSON field or Hstore even an ArrayField?
what is the best solution that people used to do with Django with same problem?
update:
I have n model fields. What I really want is to store the privacy settings of each instance on itself or some other convenient way.
I have worked on my issue, tried solutions with permissions and other relations. I have a Relationship Model and all other relationship lists are derived from the Relationship model, so I don't want to maintain a separate list of Relationships.
So my pick was to go with a Postgres JSONField or HStoreField. Since Django has good support for postgres freatures, I found these points pro for the choice I made.
JSON/HashStore can be queried with Django ORM.
The configurations are plain JSON/HashStore which are easy to edit and maintain than permissions and relations.
I found database query time taken are larger with permissions than with JSON/HStore. (hits are higher with permissions)
Adding and validating permissions per field are complex than adding/validating JSON.
At some point in future if comes a more simple or hassle free solution, I can migrate to it having whole configuration at a single field.
So My choice was to go with a configuration model.
class UserConfiguration(models.Model):
user = # link to the user model
configuration = #either an HStore of JSONFeild
Then wrote a validator to make sure configuration data model is not messed up while saving and updating. I grouped up the fields to minimize the validation fields. Then wrote a simple parser that takes the users and finds the relationship between them, then maps with the configuration to return the allowed field data (logged at 2-4ms in an unoptimized implementation, which is enough for now). (With permission's I would need a separate list of friends to be maintained and should update all the group permissions on updation of privacy configuration, then I still have to validate the permissions and process it, which may take lesser time than this, but for the cost of complex system).
I think this method is scalable as well, as most of the processing is done in Python and database calls are cut down to the least as possible.
Update
I have skinned down database queries further. In the previous implementation the relations between users where iterated, which timed around 1-2ms, changing this implementation to .value_list('relations', flat=True) cut down the query time to 400-520µs.
I also don't want these privacy options to be saved on another model, but the same so that with one query I could get the user object along with the privacy options.
I would advice you to decouple the privacy objects from the UserModel, to not mess your users data together with those options. To minimize the amount of database queries, use djangos select_related and prefetch_related.
The requirements you have defined IMO lead to a set of privacy related objects, which are bound to the UserModel. django.contrib.auth is a good point to start with in this case. It is build to be extendable. Read the docs on that topic.
If you expect a large amount of users and therefore also an even larger amount of groups you might want to consider writing the permissions resolved for one user in a redis based session to be able to fetch them quickly on each page load.
UPDATE:
I thought a little more about your requirements and came to the conclusion that you need per object permission as implemented in django-guardian. You should start reading their samples and code first. They build that on top of django.contrib.auth but without depending on it, which makes it also usable with custom implementations that follow the interfaces in django.contrib.auth.
What about something like this?
class EditorList(models.Model):
name = models.CharField(...)
user = models.ForeignKey(User)
editor = models.ManyToManyField(User)
class UserPermission(models.Model):
user = models.ForeignKey(User)
name = models.BooleanField(default=False)
email = models.BooleanField(default=False)
phone = models.BooleanField(default=False)
...
editor = models.ManyToManyField(User)
editor_list = models.ManyToManyField(EditorList)
If a user wants to give 'email' permissions to public, then she creates a UserPermission with editor=None and editor_list=None and email=True.
If she wants to allow user 'rivadiz' to edit her email, then she creates a UserPermission with editor='rivadiz' and email=True.
If she wants to create a list of friends that can edit her phone, then she creates and populates an EditorList called 'my_friends', then creates a UserPermission with editor_list='my_friends' and phone=True
You should then be able to query all the users that have permission to edit any field on any user.
You could define some properties in the User model for easily checking which fields are editable, given a User and an editor.
You would first need to get all the EditorLists an editor belonged to, then do something like
perms = UserPermissions.objects.filter(user=self).filter(Q(editor=editor) | Q(editor_list=editor_list))
First of all, in my opinion you should go for multiple models and for making the queries faster, as already mentioned in other answers, you can use caching or select_related or prefetch_related as per your usecase.
So here is my proposed solution:
User model
class User(models.Model):
name = models.CharField()
email = models.EmailField()
phone = models.CharField()
...
public_allowed_read_fields = ArrayField(models.IntegerField())
friends_allowed_read_fields = ArrayField(models.IntegerField())
me_allowed_read_fields = ArrayField(models.IntegerField())
friends = models.ManyToManyField(User)
part_of = models.ManyToManyField(Group, through=GroupPrivacy)
Group(friends list) model
class Group(models.Model):
name = models.CharField()
Through model
class GroupPrivacy(models.Model):
user = models.ForeignKey(User)
group = models.ForeignKey(Group)
allowed_read_fields = ArrayField(models.IntegerField())
User Model fields mapping to integers
USER_FIELDS_MAPPING = (
(1, User._meta.get_field('name')),
(2, User._meta.get_field('email')),
(3, User._meta.get_field('phone')),
...
)
HOW DOES THIS HELPS??
for each of public, friends and me, you can have a field in the User model itself as already mentioned above i.e. public_allowed_read_fields, friends_allowed_read_fields and me_allowed_read_fields respectively. Each of this field will contain a list of integers mapped to the ones inside USER_FIELDS_MAPPING(explained in detail below)
for friend_list_1, you will have group named friend_list_1. Now the point is the user wants to show or hide a specific set of fields to this friends list. That's where the through model, GroupPrivacy comes into the play. Using this through model you define a M2M relation between a user and a group with some additional properties which are unique to this relation. In this GroupPrivacy model you can see allowed_read_fields field, it is used to store an array of integers corresponding to the ones in the USER_FIELDS_MAPPING. So lets say, for group friend_list_1 and user A, the allowed_read_fields = [1,2]. Now, if you map this to USER_FIELDS_MAPPING, you will know that user A wants to show only name and email to the friends in this list. Similarly different users in friend_list_1 group will have different values in allowed_read_fields for their corresponding GroupPrivacy model instance.
This will be similar for multiple groups.
This will be much more cumbersome without a separate permissions model. The fact that you can associate a given field of an individual user's profile with more than one friend list implies a Many to Many table, and you're better off just letting Django handle that for you.
I'm thinking something more like:
class Visibility(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
field = models.CharField(max_length=32)
public = models.BooleanField(default=False)
friends = models.BooleanField(default=False)
lists = models.ManyToManyField(FriendList)
#staticmethod
def visible_profile(request_user, profile_user):
"""Get a dictionary of profile_user's profile, as
should be visible to request_user..."""
(I'll leave the details of such a method as an exercise, but it's not
too complex.)
I'll caution that the UI involved for a user to set those permissions is likely to be a challenge because of the many-to-many connection to friend lists. Not impossible, definitely, but a little tedious.
A key advantage of the M2M table here is that it'll be self-maintaining if the user or any friend list is removed -- with one exception. The idea in this scheme is that without any Visibility records, all data is private (to allow everyone to see your name, you'd add a Visibility record with user=(yourself), field="name", and public=True. Since a Visibility record where public=False, friends=False, and lists=[] is pointless, I'd check for that situation after the user edits it and remove that record entirely.
Another valid strategy is to have two special FriendList records: one for "public", and one for "all friends". This simplifies the Visibility model quite a bit at the expense of a little more code elsewhere.

Django custom annotation function

I want to build a simple hot questions list using Django. I have a function that evaluates "hotness" of each question based on some arguments.
Function looks similar to this (full function here)
def hot(ups, downs, date):
# Do something here..
return hotness
My models for question and vote models (relevant part)
class Question(models.Model):
title = models.CharField(max_length=150)
body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
class Vote(models.Model):
question = models.ForeignKey(Question, related_name='questions_votes')
delta = models.IntegerField(default=0)
Now, the delta attribute is either positive or negative. The hot function receives number of positive votes and number of negative votes and creation date of question.
I've tried something like this, but it isn't working.
questions = Question.objects.annotate(hotness=hot(question_votes.filter(delta, > 0),question_votes.filter(delta < 0), 'created_at')).order_by('hotness')
The error I'm getting is: global name 'question_votes' is not defined
I understand the error, but I don't the correct way of doing this.
You can't use python functions for annotations. Annotation is a computation that is done on a database level. Django provides you only a set of basic computations which can be processed by the database - SUM, AVERAGE, MIN, MAX and so on... For more complex stuffs only from version 1.8 we have an API for more complex query expressions. Before Django 1.8 the only way to achieve similar functionality was to use .extra which means to write plain SQL.
So you basically have two options.
First
Write your hotness computation in plain SQL using .extra or via the new API if your Django version is >= 1.8.
Second
Create hotness field inside you model, which will be calculated by a cron job once a day (or more often depending on your needs). And use it for your needs (the hottest list).
For those looking for an updated answer (Django 2.0+) it is possible to subclass Func to generate custom functions for aggregations as per the documentation . There is a good explanation and example here about 80% of the way through the post in the "Extending with custom database functions" section.

Where do I put a function to count foreign keys in Django?

I have a Django model object, Record, which has foreign keys to two other models RecordType and Source:
class Record(models.Model):
title = models.CharField(max_length=200)
record_type = models.ForeignKey(RecordType)
source = models.ForeignKey(Source)
Question: If I want to count the number of Record objects which refer to RecordType with id "x" and Source with id "y", where is the appropriate area of code to put that function?
Right now I have it in views.py and I feel that is a violation of best practices for "fat model, thin views", so I want to move it from views.py. But I'm not entirely sure if this is a row-based or table-based type of operation, so I'm not sure if it should be implemented as a model method, or instead as a manager.
Here's the current (working) logic in views.py:
record_count = Record.objects.filter(record_type__id=record_type_.id, source__id=source_.id).count()
Just to be clear, this isn't a question of how to get the count, but simply in which area of code to put the function.
Here's a similar question, but which was addressing "how to" not "where":
Counting and summing values of records, filtered by a dictionary of foreign keys in Django
If the result involves multiple rows, it is a table-related method, and according to Django conventions, should be a manager method.
From the Django docs:
Adding extra Manager methods is the preferred way to add “table-level” functionality to your models. (For “row-level” functionality – i.e., functions that act on a single instance of a model object – use Model methods, not custom Manager methods.)

Django: auditing models and recording admin usage

I'm trying to find the best way to audit some models in my Django project. To provide some context: the website has Accounts, Projects, and Users. Any of these can be marked as active or inactive. When the active state of the item changes I want to record the date and the user name of the staff member that changed it.
I've looked at several approaches to solving this auditing problem, but I'm hoping the StackOverflow community can point me to some better solutions. Django's built in history for the admin interface has a nice format, but isn't nearly detailed enough. It only tells me that an item has changed, but not what state it was set to:
I would love to use Django's built in history if I could change the Action message and only record actions for certain fields (while excluding others).
My next attempt to solve this auditing problem was looking into addons. Django Reversion seems to be the most popular, but I don't need most of the features it has and it's really geared towards version control rather than auditing. I also found one addon called AuditTrail, but it's very primitive.
After trying these possibilities I attempted to roll my own auditing code. I created a new model to record activity. An entry is written every time an Account, Project, or User is activated/deactivated.
class ActivityHistory(models.Model):
"""
Stores the history of when accounts, projects, and users are active.
This class uses generic relations in order to point to any model type.
For more information see: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#generic-relations
"""
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
active = models.BooleanField(help_text="The target state (true if the item was activated, false if it was deactivated).")
date = models.DateTimeField(help_text="The time at which the active state was changed.")
actor = models.ForeignKey(User, help_text="The staff member who changed the active state of the account, project, or user in question.")
class Meta:
ordering = ['-date']
The problem with this is that I have to record the information using signals. Inside a signal handler I don't have access to the HTTP request object. So how can I record which staff member changed the item in question?
Are there other solutions to this problem that would be easier to implement?
You could reimplement Django's ModelAdmin construct_change_message method and add your functionality in there, this way you leverage on existing functionality.
However this is not documented so it could change in a future Django version
https://github.com/django/django/blob/master/django/contrib/admin/options.py#L665
Rather than implement this yourself from scratch I would recommend either seeing if an existing solution can match what you need or almost get you there. Fork it, extend it for your needs and if you feel good about it share it back!
I used django-audit in my recent project and was fairly happy with it (with some tweaks). Another good choice which I would try now is django-audit-log.

Categories