I have a model with a generic relation:
TrackedItem --- genericrelation ---> any model
I would like to be able to generically get, from the initial model, the tracked item.
I should be able to do it on any model without modifying it.
To do that I need to get the content type and the object id. Getting the object id is easy since I have the model instance, but getting the content type is not: ContentType.object.filter requires the model (which is just content_object.__class__.__name__) and the app_label.
I have no idea of how to get in a reliable way the app in which a model is.
For now I do app = content_object.__module__.split(".")[0], but it doesn't work with django contrib apps.
The app_label is available as an attribute on the _meta attribute of any model.
from django.contrib.auth.models import User
print User._meta.app_label
# The object name is also available
print User._meta.object_name
You don't need to get the app or model just to get the contenttype - there's a handy method to do just that:
from django.contrib.contenttypes.models import ContentType
ContentType.objects.get_for_model(myobject)
Despite the name, it works for both model classes and instances.
You can get both app_label and model from your object using the built-in ContentType class:
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User
user_obj = User.objects.create()
obj_content_type = ContentType.objects.get_for_model(user_obj)
print(obj_content_type.app_label)
# u'auth'
print(obj_content_type.model)
# u'user'
This is better approach respect of using the _meta properties that are defined for private purposes.
Related
I want to create my own view counter. I got inspired from django-hitcount. I read all models of this app.
In these lines:
class HitCountMixin(object):
"""
HitCountMixin provides an easy way to add a `hit_count` property to your
model that will return the related HitCount object.
"""
#property
def hit_count(self):
ctype = ContentType.objects.get_for_model(self.__class__)
hit_count, created = HitCount.objects.get_or_create(
content_type=ctype, object_pk=self.pk)
return hit_count
I couldn't understand the meaning and usage of ContentType and get_for_model(self.__class__). Can anyone help me?
Source of this mixin is here.
Since HitCountMixin can be inherited by different models in your app, HitCount model must be in some way connected to these models with a relation.
Here you can think of ContentType as a way of creating dynamic relation unlike it is with e.g. ForeignKey where you are bound to use the relation only with one model (table).
get_for_model is just Django's helper method for getting ContentType instance for given model because each model (table) would have its corresponding ContentType instance.
With example model using this mixin:
class Example(models.Model, HitCountMixin):
pass
ContentType.objects.get_for_model(self.__class__) would return ContentType instace for model Example
You can read more about ContentTypes in Django documentation
I'm trying to create a custom user class and I'd like to know the best practice for implementing referencing the new user. After following the Django docs method of implementing the custom user as follows in models.py:
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
And settings.py
AUTH_USER_MODEL = 'myapp.MyUser'
And admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
admin.site.register(User, UserAdmin)
The Django docs say to use settings.AUTH_USER_MODEL in place of user specifically for ForeignKeys and OnetoOneField, but it's not clear about other instances.
My question is specific to how to refer to the custom user class in views.py. Before defining the user class I was using
from django.contrib.auth.models import User
But after defining a custom class this is no longer correct. I've seen boilerplate code use this method in the beginning of views.py:
from django.contrib.auth import get_user_model
User = get_user_model()
Is this the best practice for referencing the custom user? Or should I just be using settings.AUTH_USER_MODEL in place of where I previously had User?
Using settings.AUTH_USER_MODEL will load the user class lazily after all django apps are loaded. Calling get_user_model() on a module level when your app is initially loaded may result in that the user model app is not loaded and also circular imports.
Update: I read two specific questions:
How to correctly access the user model, contrib or custom.
Djangos get_user_model() is quite simply a call to django.apps get_model() using the settings.AUTH_USER_MODEL. If you are writing apps that might be reused in other projects with other user models, use the get_user_model call. Always. Then it doesn't matter what the user model is.
If you have created your own core.User model and is very confident that your code will only be used in this project, from core.models import User works as well.
When to use the string representation from settings instead of fetching the model.
The string representation will in the end usually call the same django.apps get_model() anyway. By giving a string instead of the class itself in Foreignkeys, OneToOneFields etc you simply don't require the model to be looked up during django app imports, where the user model may not yet be available. So using string representation is simply deferred loading of a model. The same goes for all models.
An also during djangos different major versions this behavior have changed, which is another topic. Notice that get_user_model() have been updated in Django 1.11 for import usage.
https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#referencing-the-user-model
you can go with get_user_model instead User
from django.contrib.auth import get_user_model
User = get_user_model()
get_user_model will Returns the User model that is active in this project.
if you modify(adding new field into it) default User table you need to use get_user_model it will return active User table.
BTW User will return native from django.contrib.auth.models
I am a newbie of django. I want to make a ForeignKey to the auth_user table.
I try to do that:
user = models.ForeignKey(auth_user)
It cause the error:
NameError: name 'auth_user' is not defined
So, can somebody tell me how to import auth_user.
Since foreign keys accept strings, you can use the AUTH_USER_MODEL setting in your foreign key.
from django.conf import settings
class MyModel(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
This works whether you are using the built in User model, or a custom model.
Presumably you are talking about django.contrib.auth.models.User then do
from django.contrib.auth.models import User
class MyModel(models.model):
user = models.ForeignKey(User)
If you are talking about a custom User model that comes from elsewhere, replace the import with the relevent import for that class.
THE DESCRIPTION
I'm writing an API on a Django project that will get all the LogEntry on a specific model and filter them by the specific model's field.
So my model looks like this:
from django.contrib.admin.models import ContentType, LogEntry
from django.contrib.auth.models import Group
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.db import models
class LogEntryProxy(LogEntry):
content_object = GenericForeignKey()
class Meta:
proxy = True
class HomeItem(models.Model):
# Some Fields
groups = models.ManyToManyField(Group, related_name="home_item_group")
log_entry_relation = GenericRelation(LogEntryProxy, related_query_name='log_homeitem')
In the view I need to be able to make queries that fetch LogEntryProxy items that refer to HomeItem and filter them by the groups that have access to it and serialize the results. so something like this:
log_entry_queryset = LogEntryProxy.objects.filter(log_homeitem__groups__in=user.groups.all()).distinct()
My serializer looks like this (using "djangorestframework"):
from rest_framework import serializers
class LogEntryHomeItemSerializer(serializers.ModelSerializer):
content_object = HomeItemSerializer(many=False)
object_id = serializers.IntegerField()
class Meta:
model = LogEntryProxy
fields = ('object_id', 'action_flag', 'content_object', )
And it works!
THE PROBLEM
So what's the problem you may ask!
The Problem is that LogEntry actions like create and edit work! and the API will give you the results, but when you delete a HomeItem object all the LogEntry objects pointing to it will be deleted as well, thus no delete action will be given by the api (plus all the create and edit ones pointing to that object will be deleted as well). and that's all because Django's GenericRelation doesn't support on_delete. If I delete the GenericRelatin field this doesn't happen but then I have to be able to filter the HomeItem by groups in the LogEntryProxy queryset and it can't be done without the GenericRelation.
I was wondering if anyone could tell me what to do in this situation?
Should I implement a custom logging system? or there's another way that I haven't seen it yet!
try:
homeids = HomeItem.objects.filter(groups__in=user.groups.all()).values_list('id',flat=True)
log_entry_queryset = LogEntryProxy.objects.filter(object_id__in=homeids,content_type_id=ContentType.objects.get_for_model(HomeItem).id).distinct()
If you query this way you don't need GenericRelation
Update:
The above query will not fetch logentries of delete action.
It can be done like:
from django.db.models import Q
from django.contrib.admin.models import DELETION
log_entry_queryset = LogEntryProxy.objects.filter(Q(object_id__in=home_ids,content_type_id=ContentType.objects.get_for_model(HomeItem).id) | Q(action_flag=DELETION,content_type_id=ContentType.objects.get_for_model(HomeItem).id)).distinct()
I'm trying to extend the default Django's authentication model with additional fields and functionaliy, so I've decided to just go with extending User model and writing my own authentication backend.
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
class MyUser(User):
access_key = models.CharField(_('Acces Key'), max_length=64)
This is really a basic code yet when trying to syncdb I'm cetting a strange error that Google doesn't know about :
CommandError: One or more models did not validate:
core.sldcuser: 'user_ptr' defines a relation with the model 'auth.User', which has been swapped out. Update the relation to point at settings.AUTH_USER_MODEL.
In my settings.py I've added what I guess is required :
AUTH_USER_MODEL = 'core.MyUser'
Had anyone stumbled upon this error ?
Or maybe I should use a one-to-one way, or a hybrid of 1t1 with proxy ?
What you're doing right now, is creating a subclass of User, which is non-abstract. This means creating a table that has a ForeignKey called user_ptr pointing at the primary key on the auth.User table. However, what you're also doing by setting AUTH_USER_MODEL is telling django.contrib.auth not to create that table, because you'll be using MyUser instead. Django is understandably a little confused :P
What you need to do instead is inherit either from AbstractUser or AbstractBaseUser.
Use AbstractUser if you want everything that User has already, and just want to add more fields
Use AbstractBaseUser if you want to start from a clean state, and only inherit generic functions on the User, but implement your own fields.
REF:
https://docs.djangoproject.com/en/dev/topics/auth/customizing/#extending-the-existing-user-model
https://docs.djangoproject.com/en/dev/topics/auth/customizing/#substituting-a-custom-user-model