How to set verbose_name for external app model in Django? - python

I'm trying to set verbose_name for a model SocialAuthUser from django_social.
I've tried to use proxy model, setting its Meta.verbose_name to desired value, but had no success (probably I did it wrong). If it's the way to go, I can provide more details.
It would be great to avoid installing module from pip in editable mode just to replace verbose_name in admin site.
Probably I can replace model name in admin site in some other way?
I thought about adding custom link to admin site, but didn't research this method yet because it feels hacky.

You almost got it right. For your changes on the proxy model to take effect you need to unregister the model from admin site first and then register the proxy model.
The example below is for social_django.Association model.
# admin.py
from django.contrib import admin
from social_django.admin import AssociationOption
from social_django.models import Association
class AssociationProxy(Association):
class Meta:
proxy = True
verbose_name = 'custom model'
app_label = 'social_django'
admin.site.unregister(Association)
admin.site.register(AssociationProxy, AssociationOption)
This assumes you are using the default admin site
# urls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]

Related

Django REST Swagger not showing POST methods

I'm having problem with Django REST Swagger. I've created a simple viewset for users using DRF (just for showing my problem), where AppUser is my custom user model and it is not showing the POST method in my documentation, but I can call it with Postman and create a new resource.
I'm using:
Django 2.1
Django-rest-swagger 2.2.0
Djangorestframework 3.9.1
Here is my code:
views.py
class UserViewSet(viewsets.ModelViewSet):
queryset = AppUser.objects.all()
serializer_class = UserSerializer
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = AppUser
fields = '__all__'
urls.py
from django.conf.urls import url, include
from rest_framework.routers import SimpleRouter
from rest_framework_swagger.views import get_swagger_view
import app.views as app
# creating router
router = SimpleRouter()
router.register(r'users', app.UserViewSet)
schema_view = get_swagger_view(title='My app API')
# register urls
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^docs', schema_view)
]
Here you can see what my app documentation looks like:
I would like to get something like this:
I've tried multiple tutorials on creating Swagger documentation and I was trying it on User model, but I still get only the GET request. What am I doing wrong?
Thank you for your help.
I've figured it out. I haven't been logged in properly so I haven't been authenticated against permissions listed in DEFAULT_PERMISSION_CLASSES setting for DRF in settings.py.
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES':
('rest_framework.permissions.IsAuthenticatedOrReadOnly',),
}
HTTP methods POST, PUT, PATCH, etc. are checked using has_permission() against list of permissions defined there.
After logging in it works well.
EDIT: Problem with login was, that Django-rest-swagger 2.2.0 is not working correctly with JWT authentication, so I downgraded to 2.1.2.

Showing all models in Django admin panel in development

When I am developing I constantly need to access data from the admin panel but I do not wish to add all models in admin.py since I do not want them to be accessed in production.
Is there a way to show all models in the admin panel in the development environment and hide (part of) them in production automatically?
I think that would be as simple as this:
# my_app/admin.py
from django.contrib import admin
from django.conf import settings
from .models import MyModel, AnotherModel
class MyModelAdmin(admin.ModelAdmin):
pass
class AnotherModelAdmin(admin.ModelAdmin):
pass
# conditional registration of models
if settings.DEBUG:
admin.register(MyModel, MyModelAdmin)
admin.register(AnotherModel, AnotherModelAdmin)

Add view in the bottom of Django modeladmin

I have a blog made with Django where I write posts in markdown. I would like to add a view in the bottom of the admin page for each instance of the class Entry (my blog post class) such that I can get a preview of what the markdown looks like, while I'm writing. Just as you get a preview here on Stack Overflow when you create a new post.
I already have an admin class extending ModelAdmin:
class EntryAdmin(admin.ModelAdmin):
list_display = ('title','created')
prepopulated_fields = {'slug': ('title',)}
Is it possible to modify ModelAdmin further, such that it loads a certain html file (blogpost.html) and shows it in the bottom of the admin page?
I made a picture to show exactly what I mean:
NB: I know there are various tools such as Django admin plus, that allows one to add views to the admin interface, but not for each instance of an object.
You can use markdownx for that:
pip install django-markdownx
project settings.py
INSTALLED_APPS =
#. . . .
'markdownx',
]
project urls.py
urlpatterns = [
#[...]
url(r'^markdownx/', include('markdownx.urls')),
]
and then collect static files.
python3 manage.py collectstatic
your models.py
from markdownx.models import MarkdownxField
class MyModel(models.Model):
myfield = MarkdownxField()
your app admin.py
from django.contrib import admin
from markdownx.admin import MarkdownxModelAdmin
from .models import MyModel
admin.site.register(MyModel, MarkdownxModelAdmin)
This should work.

Django (1.10) override AdminSite

I've tried to override AdminSite class with my own custom class. I followed tutorial from django's documentation: https://docs.djangoproject.com/en/1.10/ref/contrib/admin/#customizing-adminsite but it didn't work. To be specific, I'd like to override original AdminSite with my own class and not just add another admin site into my project.
I've created my custom class MyAdminSite which inherit from class
from django.contrib.admin import AdminSite
class MyAdminSite(AdminSite):
pass
Then in my app urls.py I've add:
from django.conf.urls import url, include
import django.contrib.admin as admin
from .admin_site import MyAdminSite
admin.site = MyAdminSite()
admin.autodiscover()
urlpatterns = [
url(r'^', admin.site.urls),
]
It seemed to work, but admin models are register to AdminSite insted of MyAdminSite.
I tried three ways of register models to my custom site:
#admin.register(Model)
class ModelAdmin(model.AdminModel):
...
This way models are registered to original AdminSite.
Second way:
#admin.site.register(Model):
class ModelAdmin(model.AdminModel):
...
That don't work and cause exception. The ModelAdmin class isn't passed to register method.
Last way:
class ModelAdmin(model.AdminModel):
...
admin.site.register(Model, ModelAdmin)
That works, but on admin site I can see only my models not models from Django admin (Users and Groups).
How can I permanently override admin.site and register all models to MyAdminSite?
From myapp/admin.py:
from django.contrib.auth.models import Group, User
from django.contrib.auth.admin import GroupAdmin, UserAdmin
from django.contrib.admin import AdminSite
from django.contrib import admin
from .models import MyModel #This is my app's model
# Custom admin site
class MyAdminSite(AdminSite):
site_header = 'My Project Title'
site_title = 'My Project Title Administration'
index_title = 'My Project Title Administration'
# You can add on more attributes if you need
# Check out https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#adminsite-objects
# Create admin_site object from MyAdminSite
admin_site = MyAdminSite(name='my_project_admin')
# Register the models
class MyModelAdmin(admin.ModelAdmin):
list_display = ('id', 'description')
admin_site.register(MyModel, MyModelAdmin)
# Create and register all of your models
# ....
# This is the default Django Contrib Admin user / group object
# Add this if you need to edit the users / groups in your custom admin
admin_site.register(Group, GroupAdmin)
admin_site.register(User, UserAdmin)
From myproject/urls.py
from django.conf.urls import url
from django.contrib import admin
from myapp.admin import admin_site ##! Important..Import your object (admin_site) instead of your class (MyAdminSite)
urlpatterns = [
url(r'^admin/', admin_site.urls), #Now all /admin/ will go to our custom admin
]
I didn't found the solution to my problem, but I have made a workaround.
First we need to create module in our app (e.g. admin.py) and then extend class AdminSite:
from django.contrib.admin import AdminSite
class MyAdminSite(AdminSite):
...
Then on bottom of module we need to create instance of our MyAdminSite and register built-in models from Django:
site = MyAdminSite()
site.register(Group, GroupAdmin)
site.register(User, UserAdmin)
Necessary imports:
from django.contrib.auth.models import Group, User
from django.contrib.auth.admin import GroupAdmin, UserAdmin
In our site url module we need to override original site object:
from .admin import site
admin.site = site
admin.autodiscover()
...
url(r'^admin/', admin.site.urls)
...
Last change we need to do is register our models. One thing we need to remeber is that we can't use register as decorator like that:
#admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
...
or:
#admin.site.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
...
We need to define our ModelAdmin class and then call register on our MyAdminSite object:
class MyModelAdmin(admin.ModelAdmin):
...
admin.site.register(MyModel, MyModelAdmin)
This is the only solution that is working for me.
I faced a similar problem. I used Django 2.1 and the hook from the comments above didn't work for me. And also I was not able to import GroupAdmin and UserAdmin, like so
from django.contrib.auth.models import Group, User
from django.contrib.auth.admin import GroupAdmin, UserAdmin
Importing GroupAdmin or UserAdmin broke the code for some reason. I was not able to define the exact reason.
So my workaround was (in project/urls.py):
from django.conf.urls import include, url
from django.contrib.admin import site
from project.admin import myadmin
myadmin._registry.update(site._registry)
urlpatterns = [
url(r'^admin/', myadmin.urls),
]
The idea here is to copy registered models from default admin site. Maybe it's not good to do so, but I could not find anything else working.

Serializing with django rest framework

I have the model named Artist and I want expose this model with Django Rest Framework to create an API and this data can be consumed.
I've created a class based view in artists/views.py named ArtistViewSet
#CBV for rest frameworks
from rest_framework import viewsets
class ArtistViewSet(viewsets.ModelViewSet):
model = Artist
I also have an url named api/ in the urls.py file (view third url named api/) which the user could access to the view above mentioned.
# coding=utf-8
from django.conf.urls import patterns, include, url
from django.conf import settings
from django.contrib import admin
admin.autodiscover()
from rest_framework import routers
from artists.views import ArtistViewSet
#I create a router by default
router = routers.DefaultRouter()
#Register the model 'artists' in ArtistViewSet
router.register(r'artists', ArtistViewSet)
urlpatterns = patterns('',
(r'^grappelli/', include('grappelli.urls')), # grappelli URLS
url(r'^admin/', include(admin.site.urls)),
#Include url api/ with all urls of router
url(r'^api/', include(routers.urls)),
)
When I go to my browser and type http://localhost:8000/api/ I get this message error:
What did can be happened me?
In Django REST framework 2.4+ (including 3.0+), the model attribute for views has been deprecated and removed. This means that you should be defining your view as
from rest_framework import viewsets
class ArtistViewSet(viewsets.ModelViewSet):
queryset = Artist.objects.all()
Which should give you the result you are expecting. Now, you asked in the comments
I cannot understand the role of base_name. I mean, this base name is the url that I've created? My viewset ArtistViewSet does not have a queryset attribute, due to this, according to documentation, it's necessary put the base_name argument, but i don't know how to do it.
The base_name that can be optionally defined when registering a ViewSet is used when naming the automatically generated routes. By default, the format is [base]-list and [base]-detail, where [base] is the base_name that can be defined. When you do not specify your own base_name, it is automatically generated based on the model name. As the queryset method must be defined for ViewSet instances, this is where the model (and later model name) is retrieved. As you did not provide the queryset argument, Django REST framework triggers an error because it cannot generate a base_name.
To quote from the documentation on routers
Note: The base_name argument is used to specify the initial part of the view name pattern.
The documentation goes on to further explain exactly why you are getting the issue, even including an example, and how to fix it.

Categories

Resources