I have myApp/models/profiles.py instead of myApp/models.py (to group related models)
How do you set AUTH_USER_MODEL in this case?
Because auth accepts only "foo.bar" pattern.
app_label, model_name = settings.AUTH_USER_MODEL.split('.')
Django expects the models for any given app to be in app.models. If you want to use this kind of file structure, you'll need to still make sure this is the case. The easiest way to do this is too add from profiles import * in myApp/models/__init__.py and then use AUTH_USER_MODEL as normal.
For example, you you had myApp/models/profiles.py and myApp/models/actions.py your myApp/models/__init__.py should read
from profiles import *
from actions import *
Remember to make sure you don't have any name conflicts too, and you may wish to use set your __all__ value in each of your sub-packages.
You can either pass the class directly or the 'app.model'. django only registers classes defined in app.models module, so you'll need to import the class in models init. Since you'll be importing the model in init anyway, you don't need to specify the full path to the class in the foreignkey.
Related
My app is full of statements like this:
from my_app.model import Customer
While it works, I'll like to have a way to reference the current app without hard coding the app name into the import. Something like:
from keyword_for_current_app.model import Customer
Is there a clean way of achieving this with Django?
This is more of a python question than a Django one as it relates to how imports work. As far as I know, there isn't a way to do it as you describe, unless you change the structure of your files.
However, it isn't impossible to do something using Django's functionality to dynamically import models. You could swap some of this with importlib if you need a more general import rather than just returning the model class:
from django.apps import apps
# option 1
model = apps.get_model('app.Model')
# option 2
def get_model(model_name):
return apps.get_model(f'myapp.{model_name}')
# option 3
def get_model(model_name):
for model in apps.get_models():
if model.__name__ == model_name:
return model
The problem with option 1 for your use case is that you still need to specify the app name. Option 2 is an improvement, but you are hardcoding the app name into the function. Option 3 is the best, but might be problematic if you have models with the same name but in different apps/tables.
In my opinion, you should import each model explicitly at the top of your files. This is the cleanest and most readable way - if someone were to read your code, they would expect models to be imported this way.
What Django command(s) must I use to check if model SomeModelName exists?
Django has an django.apps module with an apps class:
from django.apps import apps
this apps class has a get_models() function, that returns the Model classes (these do not include abstract models, and tables as a result of ManyToManyFields).
We can use .__name__ to obtain the classname. So we can check if SomeModelName exists with:
from operator import attrgetter
'SomeModelName' in map(attrgetter('__name__'), apps.get_models())
Note that this will specify the name of the classes, and that in the different applications you have registered, several models can have the same name (but these are not the same model).
Let's say I have two models, in different apps. App Two knows about app One, but not the other way around:
# one/models.py
from django.db import models
class One(models.Model):
pass
# two/models.py
from django.db import models
from one.models import One
class Two(models.Model):
one = models.ForeignKey(One)
I also have One registered in the admin site:
# one/admin.py
from django.contrib import admin
from .models import One
admin.site.register(One)
How do I register Two as an Inline on One's admin page, without introducing a circular dependency between the two apps?
You can do this pretty simply, providing you don't mind accessing a 'private' attribute on the ModelAdmin. (Attributes beginning with an underscore are treated as private by convention.)
# two/admin.py
from django.contrib import admin
from one.models import One
from .models import Two
class TwoInline(admin.StackedInline):
model = Two
admin.site._registry[One].inlines.append(TwoInline)
I had the same issue but solved it more gentle way.
# one/admin.py
class OneAdmin(admin.ModelAdmin):
model = One
admin.site.register(One, OneAdmin)
# two/admin.py
class TwoInline(admin.TabularInline):
model = Two
import one.admin
class OneAdmin(one.admin.OneAdmin):
inlines = [TwoInline]
admin.site.unregister(One)
admin.site.register(One, OneAdmin)
As you see I extended the original ModelAdmin from first app and added inlines from second app. Don't forget to unregister the model from first app before registering it again.
It's safe and much better than to access private member of the class as was suggested.
I would try the following:
# one/admin.py
from django.contrib import admin
from one.models import One
from two.models import Two
class TwoInline(admin.StackedInline):
model = Two
class OneAdmin(admin.ModelAdmin):
inlines = [TwoInline,]
admin.site.register(One, OneAdmin)
You can read more at the docs.
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
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.