Django Rest Framework using the url to load specified models - python

Is it possible to use generic url settings to implement the django rest interface for all models in django?
So instead of per model configuration:
class BlogResource(ModelResource):
model = Blog
urlpatterns = patterns('',
url(r'^Blog/$', ListOrCreateModelView.as_view(resource=BlogResource)),
url(r'^Blog/(?P<pk>[^/]+)/$', InstanceModelView.as_view(resource=BlogResource)),
)
A more generic type of loading:
urlpatterns = patterns('',
url(r'^(?P<model>\w+)/$', GenericView.render_model_list()),
url(r'^(?P<model>\w+)/(?P<pk>[^/]+)/$', GenericView.render_model()),
)
With something that allows the system to generate the model and render it to the rest interface.

class BlogResource(ModelResource):
model = Blog
urlpatterns = patterns('',
url(r'^Blog/$', ListOrCreateModelView.as_view(resource=BlogResource)),
url(r'^Blog/(?P<pk>[^/]+)/$', InstanceModelView.as_view(resource=BlogResource)),
)
in more general way the solution would look like this (sorry - i wrote it by hand), but you still need to import these models and form the model tuple by hand.
from django.conf.urls.defaults import patterns, url
from models import Model1, Model2, Model3
urlconf = ['', ]
for obj in (Model1, Model2, Model3):
name = obj.__class__.__name__
ResourceClass = type('%sResource' % name, (obj,), {
'model': obj,
})
urlconf.append(url(r'^%s/$' % name, ListOrCreateModelView.as_view(resource=ResourceClass)))
urlconf.append(url(r'^%s/(?P<pk>[^/]+)/$' % name, ListOrCreateModelView.as_view(resource=ResourceClass)))
urlpatterns = patterns(urlconf)

Related

Unregister a viewset from drf router

I have two separate apps Product and Tag which i used another app Product_tags to connect them together. in this way, if one of them don't exists, another one will work fine. inside Product_tags, I created a new TagProductSerializer which inherits ProductSerializer and I just added a new field named tag in fields list. product_tags/serializers.py:
class TagProductSerializer(ProductSerializer):
tags = serializers.PrimaryKeyRelatedField(queryset=Tag.objects.all())
class Meta:
model = Product
fields = [
'title',
'tags',
]
#...
and I did the same with Product viewsetproduct_tags/views.py
class TagProductViewset(ProductViewset):
serializer_class = SocialProductSerializer
and in my product_tags/urls.py I imported my Product router and i wanted to register my product viewset again for router. and there is my problem:
product/urls.py
router = routers.DefaultRouter()
router.register('product', ProductViewset)
urlpatterns = [
path('', include(router.urls)),
]
product_tags/urls.py (PROBLEM)
from product.urls import router
from .views import TagProductViewset
router.unregister('product') # I'm looking for something like this
router.register('product',TagProductViewset)
NOTE: I want to show the tags when getting product and because of that, I don't want to use different url for getting tag (e.g "api/product/tags/")
First Try:I tried to register the 'product' again (router.register('product',SocialProductViewset)) but it doesn't works
I fixed my problem but i forgot to say that here. I just created new router in my product_tags/urls.py and then I added it in urlpatterns inside of project/urls.py at top of my original product router.
product_tags/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import TagProductViewset
router = DefaultRouter()
router.register('product',TagProductViewset)
urlpatterns = [
path('', include(router.urls)),
]
project/urls.py
from django.urls import path, include
urlpatterns = [
path('api/v1/', include("product_tags.urls")),
path('api/v1/', include("product.urls")),
]

Override the automatic generated view_name in Django Rest Framework

I'm creating a Restful API with Django REST Framework. What I have is a project with two different apps. One app is called project/catalog and one is called project/environment. Both of the apps have a model called Device and their own url.py file, which will be included in the project/url.py file.
The urls from the apps are included with a namespace in project/url.py:
# project/url.py
urlpatterns = [
path(r'api/', include(('environment.urls', 'environment'))),
path(r'api/', include(('catalog.urls', 'catalog')))
]
The basename for the urls are automatic generated because in the viewSet I have a attribute queryset
My question is:
Is it possible to override the automatic view_name generation to use the pattern with a namespace? What it says in the documentation is that the view_name is generated like this (model_name)-detail for example. What I want is that the view_name will be generated like this (app_name):(model_name)-detail
I can use the namespace when I create the HyperlinkedIdentityField by my self and add the view_name attribute, like below:
# project/catalog/serializer.py
class DeviceSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='catalog:device-detail')
class Meta:
model = models.Device
fields = [
'url',
...
# project/environment/serializer.py
class DeviceSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='environment:device-detail')
class Meta:
model = models.Device
fields = [
'url',
...
But once I remove the example above it throws the Could not resolve URL for hyperlinked relationship using view name "device-detail" exception. Because the automatic generated view_name excpects device-detail, but I want it to expect catalog:device-detail for the catalog app or environment:device-detail for the environment app.
Some solutions I know about are:
Removing the namespace in project/url.py, but then there are two apps urls which has the same basename. In project/catalog the basename will be device-detail as same for project/environment. This will give issues because it will only look at one basename.
Change the HyperlinkedModelSerializer to just ModelSerializer, but that doesn't really fix my problem for my use case
Define every HyperlinkedIdentityField and add the view_name for every serializer, but that will be a pain for maintenance
My app url files
# project/catalog/url.py
from django.urls import include, path
from rest_framework import routers
from catalog import views
router.register(r'devices', views.DeviceViewSet)
urlpatterns = [
path('catalog/', include(router.urls)),
]
# project/environment/url.py
from django.urls import include, path
from rest_framework import routers
from environment import views
router.register(r'devices', views.DeviceViewSet)
urlpatterns = [
path('environment/', include(router.urls)),
]
EDIT
I have for example the AssemblySerializer in the app environment project/environment/serializer.py. Because I have two apps which can have the same urls I have to use namespaces, which is OK and what I want.
class AssemblySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.Assembly
fields = '__all__'
extra_kwargs = {
'url': {'view_name': 'environment:installedassembly-detail'},
'device': {'view_name': 'environment:device-detail'},
'parent': {'view_name': 'environment:installedassembly-detail'},
'catalog_assembly': {'view_name': 'catalog:assembly-detail'}
}
What I don't want is to set for each serializer the view_name like in the example above. I want that it will automatic look at the namespace of the app environment so that I only have to define a view_name when its outside the app, like the example below.
class AssemblySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.Assembly
fields = '__all__'
extra_kwargs = {
'catalog_assembly': {'view_name': 'catalog:assembly-detail'}
}
You want to use namespaces in this case.
If you are using recent Django then you should specify an app namespace in your urls.py using app_name="catalog". You can override this in include() if needed.
urlpatterns = [
path(r'api/environment/', include('environment.urls')),
path(r'api/catalog/', include('catalog.urls')),
]
Your urls.py can then be simplified like this:
app_name = 'catalog'
urlpatterns = router.urls
And you can reverse the lookup using that namespace (they can also be nested). Read up on setting the view_name on HyperlinkedModelSerializer:
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = ['account_url', 'account_name', 'users', 'created']
extra_kwargs = {
'url': {'view_name': 'accounts', 'lookup_field': 'account_name'},
'users': {'lookup_field': 'username'}
}
If you need to add custom urls outside the router, then just add them as normal.
urlpatterns += [
path('other/', some_view_func, )
path('ship/', include(("other_thing.urls", "ship"))
]
I highly recommend you install django_extensions installed so you can view your routes:
$> ./manage.py show_urls | column -t
...
PATH VIEW FUNC VIEW NAME
/catalog/items/ app.ItemViewSet catalog:item-list
/catalog/items/<pk>/ app.ItemViewSet catalog:item-detail
...
The include function is a little strange, I'll give you that. I have a helper that I use which makes it consistent, and usually removes the need for routers:
urlpatterns = [
route('layouts/', LayoutViewSet, name="layouts"),
route('admin/', admin_urls, name='admin'),
route('catalog/', 'apps.catalog.urls', name='catalog'),
etc
]

Django url not finding a template using CreateView in Class-Based Views

I have created a page where a user can add a new item (notes in this case) and I am making use of CBV which I have recently started learning.
This is my model form
class NoteForm(forms.ModelForm):
class Meta:
model = Note
fields = ('title', 'note', 'tags')
This is the view in views.py
class NoteCreate(CreateView):
model = Note
form_class = NoteForm
template_name = "add_note.html"
Then this is the url as I used in the urls.py of the app
from django.conf.urls import patterns, url
from . import views
from madNotes.views import NoteCreate, NoteIndex,
urlpatterns = patterns(
'',
url(r'^notes/add/$', NoteCreate.as_view(), name="new_note"),
url(r'^$', NoteIndex.as_view()),
url(r'^(?P<slug>\S+)/$', views.NoteDetail.as_view(), name="entry_detail"),
)
NB: I used the same url as the main page at 127.0.0.1:8000 in the projects urls.py file and it worked.
I have seen several tutorials and even the docs and can't seem to find what I am doing wrong. Will I also need to add a function in order for it to be saved in the db or the CBV will do it all?
EDit: The error I get is this
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/notes/add/
Here is the project's urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
from MadNotez import settings
from registration.backends.default.views import RegistrationView
from madNotes.forms import ExRegistrationForm
if settings.DEBUG:
import debug_toolbar
urlpatterns = patterns('',
url(r'^__debug__/', include(debug_toolbar.urls)),
url(r'accounts/register/$', RegistrationView.as_view(form_class = ExRegistrationForm), name='registration_register'),
url(r'^accounts/', include('registration.backends.simple.urls')),
url(r'^admin/', include(admin.site.urls)),
url('^markdown/', include('django_markdown.urls')),
url('^notes/', include('madNotes.urls')),
#url(r'^$', views.NoteCreate.as_view(), name="new note"), when I used it here it worked
)
you say that is the urls.py of the app, which means it is included by the project's urls.py.
As you show now, all the app's URIs go under the notes prefix:
url('^notes/', include('madNotes.urls')),
so as things stand at present the correct URI for the page is
http://127.0.0.1:8000/notes/notes/add/
In order to clean things up a bit, I'd suggest to modify the app's urls.py to
url(r'^add/$', NoteCreate.as_view(), name="new_note"),
so that the page can be reached at
http://127.0.0.1:8000/notes/add/
This way all the app's pages/services are available under the notes prefix and with a simple name that is consistent with their action (i.e. add, delete, etc)

Django NameError: name 'views' is not defined

I am working through this tutorial on rapid site development with Django.
I have followed it exactly (as far as I can see), but get the following error when I try to view the index page:
NameError at /name 'views' is not defined
Exception location: \tuts\urls.py in <module>, line 12
Here's urls.py:
from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^$', views.index, name='index'),
)
Here's views.py:
from django.shortcuts import render
# Create your views here.
def index(request):
items = Item.objects.order_by("-publish_date")
now = datetime.datetime.now()
return render(request,'portfolio/index.html', {"items": items, "year": now.year})
And here's models.py:
from django.db import models
# Create your models here.
class Item(models.Model):
publish_date = models.DateField(max_length=200)
name = models.CharField(max_length=200)
detail = models.CharField(max_length=1000)
url = models.URLField()
thumbnail = models.CharField(max_length=200)
I also have a basic index.html template in place. From looking around I think I need to import my view somewhere.
But I'm completely new to Django so I have no clue. Any ideas?
The error line is
url(r'^$', views.index, name='index'),
#----------^
Here views is not defined, hence the error. You need to import it from your app.
in urls.py add line
from <your_app> import views
# replace <your_app> with your application name.
from .views import index
here we have to import views model classes on urls model so above sentence import your view model class on urls model.
add this code in urls.py
If you are using rest_framework in django then import:
from rest_framework import views

Override AdminSite to append custom urls

I overrode default AdminSite class as described in manual, though it's too pure with information about this part over there.
My gs/admin.py file:
from django.contrib.admin import AdminSite
from django.conf.urls import patterns, url
from gs.views import *
class AdminSiteGs(AdminSite):
def get_urls(self):
urls = super(AdminSiteGs, self).get_urls()
urls += patterns('',
url(r'^my_admin_view/$', self.admin_view(my_admin_view))
)
return urls
admin_site_gs = AdminSiteGs()
gs it's my application and project name.
gs/urls.py file:
from django.conf.urls import patterns, include, url
from page import views
from gs.admin import admin_site_gs
urlpatterns = patterns('',
url(r'^admin/', include(admin_site_gs.urls)),
)
and I have application named page, where I place admin.py file:
from gs.admin import admin_site_gs
from page.models import Page, Menu
from django.contrib import admin
class PageAdmin(admin.ModelAdmin):
list_display = ('name', 'url', 'page_type')
class MenuAdmin(admin.ModelAdmin):
list_display = ('name', 'code')
admin_site_gs.register(Page, PageAdmin)
admin_site_gs.register(Menu, MenuAdmin)
So nothing here is working =( Neither /admin/my_admin view ( it returns 404 ), nor main admin page /admin. I don't see my models I registered in page/admin.py file.
It may sounds fun, but I tried all staff working in 3-4 hours =)) As you might guess I'm completely newbie both in Django and Python... All I want to know now is how to append custom URLs and views to my overriden class of AdminSite?
I removed autodiscover method, so now seems Django doesn't see nothing about file page/admin.py.
But the first question is more interesting, why I've had 404 error on trying to access the /admin/my_admin page ...
PS why my greeting at the beginning has been cutted o_O
According to the docs, any URL patterns you define for custom admin views must be occur before the admin patterns: https://docs.djangoproject.com/en/1.4/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_urls
Try:
def get_urls(self):
urls = super(AdminSiteGs, self).get_urls()
my_urls = patterns('',
url(r'^my_admin_view/$', self.admin_view(my_admin_view))
)
return my_urls + urls
You shouldn't need to include these patterns like this:
urlpatterns = patterns('',
url(r'^admin/', include(admin_site_gs.urls)), # not needed
)
In my case, I had to override the default 'add url' in order to redirect to a custom Django admin page when clicking '+Add' button in the admin.
So if I just override get_urls() in the way #Brandon said, it will return a list with a duplicate 'add' url (the custom one and the one retrieved from the super).
def get_urls(self):
info = self.model._meta.app_label, self.model._meta.model_name
urls = super(RetailerAdmin, self).get_urls()
# We need to remove the original 'add_url' in order to use the custom one.
urls.remove(urls[1])
custom_url = [
url(r'^batch/$', self.admin_site.admin_view(self.batch_upload_retailers),
name='%s_%s_add' % info),
]
return custom_url + urls
To solve this, I removed the original 'add' url (notice that the 'add' url is always in position 1).

Categories

Resources