Django Rest Framework Reverse() Method Fails - django

My main urls.py is located here ahlami -> ahlami -> urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('api/accounts/', include('accounts.api.urls')),
]
My accounts app urls.py is located here ahlami -> accounts -> api -> urls.py
urlpatterns = [
path('users/<int:pk>/', views.UserView.as_view(), name='user-detail')
]
One of my accounts views.py returns
token = Token.objects.create(......)
return Response(data=AnonymousUserTokenResponseSerializer(instance=token).data)
My token model has three fields only. For simplicity, I listed one field below
class Token(rest_framework.authtoken.models.Token):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE
AnonymousUserTokenResponseSerializer is linked to the Token model and returns three json attributes
class AnonymousUserTokenResponseSerializer(serializers.ModelSerializer):
user_id = serializers.ReadOnlyField(source='user.id')
user_url = reverse(viewname='user-detail')
class Meta:
model = Token
fields = ('key',
'user_id',
'user_url')
AnonymousUserTokenResponseSerializer fails because it can't identify reverse()
user_url = reverse(viewname='user-detail')
python manage.py runserver throws the error below because of the line above
django.core.exceptions.ImproperlyConfigured: The included URLconf
'ahlami.urls' does not appear to have any patterns in it. If you see
valid patterns in the file then the issue is probably caused by a
circular import.
My settings is located here ahlami -> ahlami -> settings -> base.py and base.py has this
ROOT_URLCONF = 'ahlami.urls'
I expect to get an output that looks like but couldn't because of the error above.
{
"key": "891e388399f2fcae016fe6887107034239041478",
"user_id": 29,
"user_url": http://localhost/api/accounts/users/29
}
How can I resolve this error and make reverse() work?
django.core.exceptions.ImproperlyConfigured: The included URLconf
'ahlami.urls' does not appear to have any patterns in it. If you see
valid patterns in the file then the issue is probably caused by a
circular import.

Use serializers.HyperlinkedIdentityField
class AnonymousUserTokenResponseSerializer(serializers.ModelSerializer):
user_id = serializers.ReadOnlyField(source='user.id')
user_url = serializers.HyperlinkedIdentityField(
view_name='user-detail',
source='user.id',
lookup_field='pk'
)
class Meta:
model = Token
fields = ('key', 'user_id', 'user_url')

Related

Django Nested Admin returns 404 or doesn't inline models in django admin area

I'm trying to get django nested admin to work, but I'm having a few issues and I'm sure I'm just making a silly mistake. Here are the steps that I followed:
Step 1: I did a pip install
Step 2: I added it to the bottom of my Installed Apps in my settings.py
Step 3: I added it to my URL array:
Their Example:
urlpatterns = patterns('',
# ...
url(r'^_nested_admin/', include('nested_admin.urls')),
)
My implementation:
urlpatterns = [
path('admin/', admin.site.urls),
path('', include("estimate_maker.urls")),
path('nested_admin/', include('nested_admin.urls')),
]
Step 4: I created a static folder in my settings.py
Step 5: I ran the collectstatic command
Step 6: I set up my admin.py in my project folder:
from django.contrib import admin
from .models import MoldInspection, MoldService
import nested_admin
class MoldServiceInline(nested_admin.NestedStackedInline):
model = MoldService
class MoldInspectionInline(nested_admin.NestedModelAdmin):
model = MoldService
sortable_field_name = "position"
inlines = [MoldServiceInline]
admin.site.register(MoldInspection)
admin.site.register(MoldService)
I do get a warning from pycharm saying the below that I'm not sure how to diagnose as I'm setting up the class as it is done in the guide.
Cannot find reference 'NestedModelAdmin' in '__init__.py'
Looking in the referenced __init__.py I see:
# import mapping to objects in other modules
all_by_module = {
'nested_admin.forms': (
'SortableHiddenMixin'),
'nested_admin.formsets': (
'NestedInlineFormSet', 'NestedBaseGenericInlineFormSet'),
'nested_admin.nested': (
'NestedModelAdmin', 'NestedModelAdminMixin', 'NestedInlineAdminFormset',
'NestedInlineModelAdmin', 'NestedStackedInline', 'NestedTabularInline',
'NestedInlineModelAdminMixin', 'NestedGenericInlineModelAdmin',
But when I update my admin.py to:
class MoldInspectionInline(nested_admin.nested.NestedModelAdmin):
I get the same error, this time pointing to "nested."
When I try to access the nested admin by either going to /nested-admin, I just get a 404 with this error message:
Using the URLconf defined in app.urls, Django tried these URL patterns, in this order:
admin/
[name='home']
nested-admin ^server-data\.js$ [name='nesting_server_data']
And when I go to /admin it looks the same as it did before.
A few more details:
I want my MoldService to exist just to be a parent for children services so I have it set up like this:
class MoldService(models.Model):
title = "Mold Services"
def __str__(self):
return self.title
I then have my child class set up like this:
class MoldInspection(models.Model):
title = "Mold Inspection"
description = models.TextField(null=True)
def __str__(self):
return self.description
Why do you think the nested admin isn't working for me?
In the referenced __init__.py file it contains the following comment
# All this craziness is so that we can allow the classes in nested_admin.formsets
# to be importable directly from this module
I guessed that something going on here is the issue, so I just deleted the contents of __init__.py and then imported with
from nested_admin.nested import *
works now!

Django REST framework RetrieveAPIView gets empty "id" parameter and returns 404 error

I use Django 1.11 + Django REST Framework. I'm trying to get detailed data about distinct record using generic RetrieveAPIView from Django REST Framework and server returns "HTTP 404 Not Found" result, i.e. no data.
models.py file:
class TestPage( models.Model):
title = models.CharField( size = 120)
counter = models.IntegerField( default = 0)
def __unicode__(self):
return u'' + self.title
serializers.py file:
class TestPageSerializer(serializers.ModelSerializer):
class Meta:
fields = ('id', 'title', 'counter',)
model = models.TestPage
urls.py file:
urlpatterns = [
url( r'^admin/', admin.site.urls),
url( r'^api/', include( 'rest_framework.urls')),
url( r'^api/', include( 'testpages.urls')),
]
testpages/urls.py file:
urlpatterns = [
url( r'^$', views.TestPageList.as_view()),
url( r'^(?P<id>)\d+/$', views.TestPageDetail.as_view()),
]
and at last views.py file:
class TestPageList(generics.ListAPIView):
lookup_field = 'id'
queryset = TestPage.objects.all()
serializer_class = TestPageSerializer
class TestPageDetail(generics.RetrieveAPIView):
lookup_field = 'id'
queryset = TestPage.objects.all()
serializer_class = TestPageSerializer
# def get_object( self):
# print self.kwargs
If I request "http://127.0.0.1:8000/api/" for all records in the list, server returns all records. But if I want to get record by id using "http://127.0.0.1:8000/api/1/" for example, I get the empty result like in the photo below.
[![enter image description here][1]][1]
If I uncomment get_object()-function in last 2 lines, I can see {'id': u''} in terminal i.e. server gets empty parameter 'id'.
What wrong with this code?
The issue is your regular expression matching for id, you're putting \d+ outside of the matching group when it's actually what you want to match:
url(r'^(?P<id>\d+)/$', views.TestPageDetail.as_view())
By the way, to be good REST citizen, you shouldn't add the / at the end of a URL for a specific object.
Note: As mentioned by JPG, adding a name for the resource you're fetching (plural) would be proper RESTful:
url(r'^pages/(?P<id>\d+)$', views.TestPageDetail.as_view())
This way, you fetch page nr. 1 at /api/pages/1
The original problem was the closing parathesis (the regex expression), you were misplaced that.
urlpatterns = [
url(r'^$', views.TestPageList.as_view()),
url(r'^(?P<id>\d+)/$', views.TestPageDetail.as_view()),
^^^ here
]
Apart from that, You should change the urls patterns to a sensible form, as
#testpages/urls.py
urlpatterns = [
url(r'^test/$', views.TestPageList.as_view()),
url(r'^test/(?P<id>\d+)/$', views.TestPageDetail.as_view()),
]
then the list-api will be available on /api/test/ endpoint and the detail-api will be available on /api/test/{id}/

Migration file don't change Site name and domain

I have this migration file:
from django.db import migrations
from django.contrib.sites.models import Site as DjangoSite
def change_django_site(apps, schema_editor):
site = DjangoSite.objects.get(id=1)
site.delete()
DjangoSite.objects.get_or_create(id=1, domain='www.xprende.com', name='XPrende')
def create_homepage(apps, schema_editor):
# Get models
ContentType = apps.get_model('contenttypes.ContentType')
Page = apps.get_model('wagtailcore.Page')
Site = apps.get_model('wagtailcore.Site')
HomePage = apps.get_model('home.HomePage')
# Delete the default homepage
# If migration is run multiple times, it may have already been deleted
Page.objects.filter(id=2).delete()
# Create content type for homepage model
homepage_content_type, __ = ContentType.objects.get_or_create(
model='homepage', app_label='home')
# Create a new homepage
homepage = HomePage.objects.create(
title="Home",
draft_title="Home",
slug='home',
content_type=homepage_content_type,
path='00010001',
depth=2,
numchild=0,
url_path='/home/',
)
# Create a site with the new homepage set as the root
Site.objects.create(
hostname='www.xprende.com', site_name='XPrende', root_page=homepage, is_default_site=True
)
def remove_homepage(apps, schema_editor):
# Get models
ContentType = apps.get_model('contenttypes.ContentType')
HomePage = apps.get_model('home.HomePage')
# Delete the default homepage
# Page and Site objects CASCADE
HomePage.objects.filter(slug='home', depth=2).delete()
# Delete content type for homepage model
ContentType.objects.filter(model='homepage', app_label='home').delete()
class Migration(migrations.Migration):
dependencies = [
('home', '0001_initial'),
('sites', '0002_alter_domain_unique'),
]
operations = [
migrations.RunPython(
create_homepage,
remove_homepage,
change_django_site,
),
]
As you can see i made a function that have to change the django site name and domain. But it doesn't do it, after apply the migrations i get the same default values:
In [3]: DjangoSite.objects.get(id=1)
Out[3]: <Site: example.com>
In [4]: DjangoSite.objects.get(id=1).name
Out[4]: 'example.com'
In [5]: DjangoSite.objects.get(id=1).domain
Out[5]: 'example.com'
In [6]: DjangoSite.objects.all()
Out[6]: <QuerySet [<Site: example.com>]>
I can't understand why, i put the sites migrations as dependencie of this migration file but still it doesn't works.
Can anyone explain me why i have this error?
Thank you.
You have to run change_django_site as separate operation:
class Migration(migrations.Migration):
dependencies = [
('home', '0001_initial'),
('sites', '0002_alter_domain_unique'),
]
operations = [
migrations.RunPython(change_django_site, reverse_code=migrations.RunPython.noop),
migrations.RunPython(create_homepage, reverse_code=remove_homepage),
]
and change_django_site must be
def change_django_site(apps, schema_editor):
DjangoSite = apps.get_model('sites', 'Site')
site = DjangoSite.objects.get_or_create(id=1)
site.domain='www.xprende.com'
site.name='XPrende'
site.save()

Adding urls/views to a model - Django Oscar?

I want to add some urls/views to dashboard app. So I forked the app using oscar_fork_app. I have created an app.py file in forked dashboardapp folder. Also added an app.py inside catalogue sub-app inside dashboard app. Following this.
My #dashboard/app.py
from oscar.apps.dashboard.app import DashboardApplication as CoreDashboardApplication
class DashboardApplication(CoreDashboardApplication):
name = 'dashboard'
permissions_map = {
'index': (['is_staff'], ['partner.dashboard_access']),
}
index_view = get_class('dashboard.views', 'IndexView')
catalogue_app = get_class('dashboard.catalogue.app', 'application')
def get_urls(self):
urls = [
url(r'^catalogue/', include(self.catalogue_app.urls)),
]
return self.post_process_urls(urls)
application = DashboardApplication()
My #dashboard/catalogue/app.py
from oscar.apps.dashboard.catalogue.app import CatalogueApplication as CoreCatalogueApplication
class CatalogueApplication(CoreCatalogueApplication):
name = None
csv_upload_view = get_class('dashboard.catalogue.views',
'CSVUpload')
def get_urls(self):
urls = [
url(r'^csvupload/$',
self.csv_upload_view.as_view(),
name='csvupload'),
]
return self.post_process_urls(urls)
application = CatalogueApplication()
when I hit /dashboard it says
Page not found (404)
Request Method: GET
Request URL: http://localhost:8000/dashboard/
Using the URLconf defined in teashop.urls, Django tried these URL patterns, in this order:
^i18n/
^admin/
^accounts/reviews/$ [name='review-list']
^accounts/subscriptions/$ [name='subscriptions']
^accounts/storecredit/$ [name='storecredit']
^catalogue/
^basket/
^checkout/
^accounts/
^search/
^dashboard/ ^catalogue/
^offers/
The current URL, dashboard/, didn't match any of these.
Do I have to copy paste all the urls in oscar.apps.dashboard.app to my forked_app ? What's the way to extend in the right way ?
I had to add all the urls of the parent class.
class DashboardApplication(CoreDashboardApplication):
...
catalogue_app = get_class('dashboard.catalogue.app', 'application')
def get_urls(self):
urls = super(DashboardApplication, self).get_urls()
urls += [
url(r'^catalogue/', include(self.catalogue_app.urls)),
]
return self.post_process_urls(urls)
Similarly for the other app.py
class CatalogueApplication(CoreCatalogueApplication):
name = None
csv_upload_view = get_class('dashboard.catalogue.views',
'CSVUpload')
def get_urls(self):
urls = super(CatalogueApplication, self).get_urls()
urls += [
url(r'^csvupload/$',
self.csv_upload_view.as_view(),
name='csvupload'),
]
return self.post_process_urls(urls)
application = CatalogueApplication()

tastypie return empty resource_uri in get

This is the resource
class TagResource(ModelResource):
user = tastypie.fields.ForeignKey(UserResource,'user')
class Meta:
queryset = Tag.objects.all()
resource_name = 'tag'
authorization= Authorization()
object_class = Tag
filtering = {
'name' : ALL,
}
simple get request
http://localhost:8000/api/v1/tag/1/?format=json
returns with empty resource_uri
{"created": "2014-03-26T15:14:11.928068",
"id": 1, "name": "test",
"resource_uri": "", "user": ""}
Why is that ?
I tried
def hydrate_resource_uri(self, bundle):
return bundle.get_resource_uri()
It didn't work and i'm pretty sure it's not supposed to require special care.
What am i missing ?
I know this is old, but I know your problem, I just had it on mine, you have a "namespace" on the API URL include or on any URL includes further up in your URL tree.
I had the same problem, and it was because I forgot to register my resource in the urls.py.
Ensure you have something like this in your urls.py file:
myapi.register(TagResource())
I have created a blog for highlighting the same problem. link to my blog
Tastypie give couple of options to create a Model Resource.
ModelResource
NamespacedModelResource
When namespace is included in urls.py then Tastypie logic of generating resource_uri fails as it also expects the same namespace in the api urls. To overcome this problem either one has to remove the namespace from module level urls.py or implement namespace with Tastypie. First solution looks easy but it may break your application. The below code will help you use second approach.
Api.py
from tastypie.resources import NamespacedModelResource
class EntityResource(NamespacedModelResource):
class Meta:
queryset = Entity.objects.all()
allowed_methods = ['get']
resource_name = 'entity'
authentication = SessionAuthentication()
authorization = Authorization()
mymodule/url.py
from tastypie.api import NamespacedApi
from mymodule.api import EntityResource
v1_api = NamespacedApi(api_name='v1', urlconf_namespace='mymodule')
v1_api.register(EntityResource())
urlpatterns = patterns('',
url(r'^api/', include(v1_api.urls)),
)
Make sure you use same namespace for module and its api urls. The above code will surely generate proper resource_uri.
Try to remove the object_class, I think that if this is ModelResource, you don't need this.
This may be because, for some reason, the api_name is missing.
Try to add it in the resource meta.
For instance, if your resource uri is /api/v1/YourResourceName, try to add in your resource Meta:
api_name = 'v1'
Hope it helps.
Only if someone else get this problem caused by a namespace in urls.py. You have to use NamespacedModelResource instead of ModelResource:
from tastypie.resources import NamespacedModelResource
class TagResource(NamespacedModelResource):
user = tastypie.fields.ForeignKey(UserResource,'user')
class Meta:
queryset = Tag.objects.all()
resource_name = 'tag'
authorization= Authorization()
object_class = Tag
filtering = {
'name' : ALL,
}
and then into your module's urls.py
from tastypie.api import NamespacedApi
v1_api = NamespacedApi(api_name='v1', urlconf_namespace='your_module')
v1_api.register(TagResource())
urlpatterns = patterns(
'',
url(r'^api/', include(v1_api.urls)),
)
Check this post.