Override the Admin.py file of reusable app in Django - django

I basically have 2 external Django app that are overriding the UserAdmin model.
Each one of these will first unregister the UserAdmin model and then register their own, something like this:
admin.site.unregister(get_user_model())
admin.site.register(get_user_model(), ExternalAppUserAdmin)
I also, in turn, override the UserAdmin:
admin.site.unregister(get_user_model())
admin.site.register(get_user_model(), MyAppUserAdmin)
The problem is that depending on the order of the INSTALLED_APPS in the settings file only the last app will actually override the UserAdmin.
Most of the times these overriding are just InlineModelAdmin inside the AdminUser, so what I've done so far is to import the external app's InlineModelAdmin models and insert them in my own admin.py. For example:
from relationships.admin import RelationshipInline
....
class BaseProfileAdminStacked( NestedModelAdmin, GuardedModelAdmin):
inlines = [BaseProfileInline, RelationshipInline,]
....
admin.site.unregister(get_user_model())
admin.site.register(get_user_model(), BaseProfileAdminStacked)
But this seems a little hacky to me, because for example:
The first external app will unregister the UserAdmin, then register it again with its own customization
The second external app will do the same, actually overriding the first one
My own app should then import all the AdminInline from the external apps and include them in my own registration of the UserAdmin
Isn't this a waste of effort? Do you have a better idea to do so?

Related

How can I add inlines to the ModelAdmin of another app, without a circular dependency?

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.

how to hide some models in django grappelli admin?

How to hide some of the models?
I need them registred to be used by other models, while calling via FK or nested etc.
I found one solution which is rather much about view layer of (MVC)
http://blog.jholster.com/post/1534211028/hide-app-names-in-django-admin
I would like to set it in admin.py, that some of registred models are hidden.
If the models are in your application, just don't register them in the first place. If the model is in a third party app like the django.contrib.auth then use AdminSite unregister method. You can put this in any admin.py or urls.py important is to be discovered by the admin.autodiscover.
# admin.py
from django.contrib.auth.models import User
admin.site.unregister(User)

How to properly extend django User model

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

having models directory and AUTH_USER_MODEL

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.

How to have 2 different admin sites in a Django project?

I want to have 2 separate admin sites inside a Django project.
By separate I mean - they should have separate users authentication, they should administer different models, and have different looks and URLs.
The reason I want to do it is the customer wants separate section to administer the CMS part of the page, and separate to use as a 'back-office' solution.
I thought about just making a copy od django.contrib.auth appliaction in my project tree, naming it differently and using separate admin.site.register() calls for both of them. This way I can have other models available in each one of them, diffrent looks, etc. I don't know how to solve the user-authentication problem (I should have different user to be able to log into CMS then into the BackOffice).
Anyone happened to do this before and could give me some hint? Or what I plan to do is just wrong by design?
You can subclass Django's AdminSite (put it eg. in admin.py in your project root):
from django.contrib.admin.sites import AdminSite
class MyAdminSite(AdminSite):
pass
#or overwrite some methods for different functionality
myadmin = MyAdminSite(name="myadmin")
At least from 1.9 on you need to add the name parameter to make it work properly. This is used to create the revers urls so the name has to be the one from the urls.py.
Then you can use it in your app's admin.py the same way as you do with the normal AdminSite instance:
from myproject.admin import myadmin
myadmin.register(MyModel_A)
You also need to define some urls for it (in your project's urls.py):
from myproject.admin import admin, user_site
from myproject.admin import myadmin
urlpatterns = patterns('',
...
(r'^admin/', include(admin.site.urls)),
(r'^myadmin/', include(myadmin.urls)),
Also see this: http://docs.djangoproject.com/en/dev/ref/contrib/admin/#adminsite-objects
To register models in different AdminSites you just need to create different instances of django.contrib.admin.sites.AdminSite, see this.
You will be good to go with two different admin sites managing different models and having different templates.
For authentication and permissions you should be able to use the build-in django.contrib.auth as is with custom permissions (hope someone else will be able to help more here)
I'm not sure that my finding, reported here, would have been entirely helpful to kender because, among other things, I don't know if he was talking not only about two admin sites but also two databases, one for each. That's my situation. I got the bright idea that I wanted one of my apps, a new app, to have its own database and own admin pages.
But I ran into a problem with the AdminSite subclassing approach of Bernhard Vallant, though it seems to be the orthodox and essentially correct thing to do. I resolved the problem.
Here's the mod to Bernhard Vallant's code that I found to be utterly necessary:
from django.contrib.admin.sites import AdminSite
class MyAdminSite(AdminSite):
pass
#or overwrite some methods for different functionality
myadmin = MyAdminSite(name='anything')
Yes, I do really mean name='anything' that you choose (as long as it isn't 'admin'). And I've toggled in and out with it and it fails every time without the anything-but-admin name assignment.
The symptoms that I acquired were that when I added the second database and created a myadmin for it and then registered the model with myadmin.register(My_ModelA), and went to look at the two admin app pages, the one for my new app that used the second database and myadmin and the model My_ModelA looked fine, but my old admin page showed dead links for its models and when I clicked there on a non-dead link for an app (an old app that uses the old database) I got a 404 code to the effect that the page didn't exist.
Also, I don't know that it matters, but I did something different from what
Bernhard Vallant did in the project urlconf:
from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include('mynewapp.urls')),
url(r'^someword/admin/', include(admin.site.urls)),
)
OK, "someword" is irrelevant--- there for appearances with regard to the end user and not the name of an app or the project. But the associated admin is the one for my old app and old database. Note the autodiscover() inclusion. There's some murky language in the docs to which Bernhard Vallant linked regarding its use when the project urlconf is configured as Bernhard Vallant has it with the myadmin import but also with a reference to the default admin.
And for the urlconf for mynewapp I have:
from django.conf.urls import patterns, url, include
from myproject.admin import myadmin
urlpatterns = patterns('',
url(r'/', include(myadmin.urls) )
)
urlpatterns += patterns('mynewapp.views',"... url() stuff for mynewapp's views"),
)
Notwithstanding the utter necessity of naming your AdminSite instance internally to something other than 'admin', I must add that when it came time to jazz up the mynewapp's admin.py file with some admin.ModelAdmin subclassing, it was necessary to indeed use admin.ModelAdmin as the parent class. myadmin is after all an instance of a subclass of AdminSite. As such I gather that it's on a par with admin.site, not with admin.
This is all very confusing to a NOOB like me because admin, with the lower case, looks like an instance, and I am unfamiliar with subclassing instances. So I assume that it isn't.