DRF: How to use urls with UUID - python

So, I would like top stop ursing urlpatterns and just use router. But instead of using ID of an object I'm using UUID instead and I'm using it with urlpatterns and dont't find some way to use it with routers.
this is my current model:
class Board(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=200, blank=False, null=False)
this is my core app urls.py:
...
router = DefaultRouter()
router.register(r'boards', BoardViewSet)
router.register(r'y', yViewSet)
router.register(r'z', zViewSet, basename='z')
urlpatterns = [
path('', include(router.urls)),
path('board-list/<uuid:pk>/', BoardViewSet.as_view({'get': 'list'}), name='boards'),
]
and this is the project urls.py:
from django.contrib import admin
from django.urls import path, include
from core.urls import router as api_router
routes = []
routes.extend(api_router.urls)
urlpatterns = [
path('api/', include((routes, 'board_microservice'), namespace='v1')),
path('admin/', admin.site.urls),
]
the application usage is ok, but I have some troubles with test.
i.e:
this works well:
url = reverse('v1:board-list')
response = api_client().get(
url
)
and it isn't working:
board = baker.make(Board)
url = reverse('v1:board-list', kwargs={"pk": board.id})
response = api_client().get(url)
I receive
django.urls.exceptions.NoReverseMatch: Reverse for 'board-list' with keyword arguments
and I think I can replace urlpatterns by router to solve it and turns it more simple
There is any way to do it with router?

You haven't shared your view, but it seems you are using a ModelViewSet and you seem to be looking for the retrieve endpoint rather than list (judging from using a pk). If so then you want to use -detail instead of -list:
url = reverse('v1:board-detail', kwargs={"pk": board.id})

Board_list is not what you want to call... Try board_get instead.
Board list takes no argument. But get does.. To get a particular board.

Related

Django,DRF, get another app's view name for a reverse

I'm trying to create a link for another app in my serializer using the solution provided here:
https://stackoverflow.com/a/45850334/12177026
I'm trying to match the view's name but every way I try I get this error:
Reverse for 'KnownLocationView' not found. 'KnownLocationView' is not a valid view function or pattern name.
serializers:
class MissionSerializer(HyperlinkedModelSerializer):
gdt = ChoiceField(choices=lazy(get_locations, tuple)())
location = SerializerMethodField(label='Open Location')
def get_location(self, obj):
request = self.context.get('request')
return request.build_absolute_uri(reverse('KnownLocationView', kwargs={'Name': obj.gdt}))
class Meta:
model = Mission
fields = ('MissionName', 'UavLatitude', 'UavLongitude', 'UavElevation', 'Area',
'gdt', 'location')
KnownLoction/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import KnownLocationView
app_name = 'KnownLocation'
router = DefaultRouter()
router.register(r'knownlocations', KnownLocationView)
urlpatterns = [
path('', include(router.urls)),
]
I tried replacing view_name with either of the following:
'knownlocations'
'KnownLocation:knownlocations'
'KnownLocation:KnownLocationView'
But get the same error
even tried to reorder the installed apps.
api/urls.py:
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')),
path('', include('landingpage.urls')), # API Landing Page
path('', include('ThreeLocations.urls')), # Triangulation between two Known GDTs and uav location.
path('', include('SecondGDT.urls')), # Options For Second GDT Positioning.
path('', include('KnownLocation.urls', namespace='knownlocations')),
# Add Known Location To the map.
] + staticfiles_urlpatterns()
KnownLocation/views.py
from rest_framework.renderers import AdminRenderer
from rest_framework.viewsets import ModelViewSet
from .models import KnownLocation
from .serializers import KnownLocationSerializer
class KnownLocationView(ModelViewSet):
"""
List of Known Locations In which we can place one of our ground positions.
press create to add a new location.
"""
serializer_class = KnownLocationSerializer
queryset = KnownLocation.objects.all()
def get_serializer_context(self):
data = KnownLocationSerializer(self.queryset, many=True, context={'request': self.request})
return data
renderer_classes = [AdminRenderer]
I think you should try to change how you are calling the reverse.
You can find reference to it below
Note: If using namespacing with hyperlinked serializers you'll also need to ensure that any view_name parameters on the serializers correctly reflect the namespace. In the examples above you'd need to include a parameter such as view_name='app_name:user-detail' for serializer fields hyperlinked to the user detail view.
The automatic view_name generation uses a pattern like %(model_name)-detail. Unless your models names actually clash you may be better off not namespacing your Django REST Framework views when using hyperlinked serializers.
Try using this
reverse('KnownLocationView:mission-detail, kwargs={'Name': obj.gdt})
Or
reverse('mission-detail, kwargs={'Name': obj.gdt})

Password_reset_done error when attempting to create reset password authentication

I want to allow the user to reset password when the user is signed out and cannot remember the password.
I am using the django authentication framework and have created the reset_password and password_reset_done mappers.
Issue : Though I have created the password_reset_done function I continue to get the below error. Is there a step that I missed that is causing this error? I do not know what I have done wrong.
I have posted all the code that I think is relevant to what I attempting to do.
Edit with full TraceBack:
Here is the code :
relative urls.py
from django.conf.urls import url
from . import views
from django.contrib.auth.views import login, logout, password_reset, password_reset_done
urlpatterns = [
url(r'^$', views.vedic_view, name = 'vedic_home_view'),
url(r'^login/$', login, {'template_name' : 'exist/login.html'}, name = 'login'),
url(r'^logout/$', logout, {'template_name' : 'exist/logout.html'}, name = 'logout'),
url(r'^register/$', views.register_view, name = 'register'),
url(r'^profile/$', views.view_profile, name = 'view_profile'),
url(r'^profile/edit/$', views.edit_profile, name = 'edit_profile'),
url(r'^change-password/$', views.change_password, name = 'change_password'),
url(r'^reset-password/$', password_reset, name = 'reset_password'),
url(r'^reset-password/done/$', password_reset_done, name = 'password_reset_done')
]
main urls.py
from django.conf.urls import url
from django.contrib import admin
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
url(r'^admin/', admin.site.urls),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
from django.conf.urls import include
from django.views.generic import RedirectView
urlpatterns += [
url(r'^exist/', include('exist.urls', namespace = 'exist')),
url(r'^$', RedirectView.as_view(url='/exist/', permanent=True)),
]
password_reset_done is within the exists namespace. It looks like you are trying to reverse the named URL without including the namespace argument somewhere. We would need to see your full traceback to see exactly where that is happening.
Since you are using the built-in auth views, the easiest fix would probably be to move your password reset handling up to your main urls.py. Notice in your traceback that the built-in password_reset view does this:
post_reset_redirect = reverse('password_reset_done')
The default implementation here is to reverse to password_reset_done without any namespace. Moving the relevant URLs up to your main urls.py will allow them to be accessed via reverse without a namespace argument, making the built-in views happy.
I realize what the issue is.
For the built-in reset view there is a post_reset_redirect variable that uses the default implementation reverse(password_reset_done) to go to the password_reset_done view.
The issue is that in my main urls.py document I created the namespace variable
namespace = exist
However I did not override the default post_reset_redirect implementation
from reverse(password_reset_done) to reverse(exist:password_reset_done).
So my current
url(r'^reset-password/$', password_reset, name = 'reset_password'),
should now look like
url(r'^reset-password/$', { 'post_reset_redirect':
'exist:password_reset_done'}, password_reset, name =
'reset_password')
As I see, you didn't include relative.url in your main urls.
EDIT
in relative urls
app_name='exists' #with this name you can call in main urls
urlpatterns = [
url(r'^$', views.vedic_view, name = 'vedic_home_view'),
#...
In main url:
urlpatterns = [
url(r'^exists/', include('exists.urls')),
#...
EDIT 2
Here'sthe docs on the subject, it explains it better than me with examples.

Add context to every Django Admin page

How do I add extra context to all admin webpages?
I use default Django Admin for my admin part of a site.
Here is an url entry for admin:
urlpatterns = [
url(r'^admin/', admin.site.urls),
]
And my apps register their standard view models using:
admin.site.register(Tag, TagAdmin)
My problem, is that I want to display an extra field in admin template header bar and I have no idea how to add this extra context.
My first bet was adding it in url patterns like below:
urlpatterns = [
url(r'^admin/', admin.site.urls, {'mycontext': '123'}),
]
But that gives an error:
TypeError at /admin/tickets/event/4/change/
change_view() got an unexpected keyword argument 'mycontext'
Can you give any suggestion? I really do not want to modify every AdminModel class I have to insert this context, as I need it on every admin page.
Thanks.
Found the solution, url registration has to be:
urlpatterns = [
url(r'^admin/', admin.site.urls, {'extra_context': {'mycontext': '123'}}),
]
Its a context dictionary inside of a dictionary with 'extra_context' as a key.
Another technique, more complex but allows different context per request (probably unavailable at OP time):
my_project/admin.py (create if missing)
from django.contrib import admin
from django.contrib.admin.apps import AdminConfig
class MyAdminConfig(AdminConfig):
default_site = 'my_project.admin.MyAdminSite'
class MyAdminSite(admin.AdminSite):
def each_context(self, request):
context = super().each_context(request)
context.update({
"whatever": "this is",
"just a": "dict",
})
return context
settings.py
INSTALLED_APPS = [
...
'my_project.admin.MyAdminConfig', # replaces 'django.contrib.admin'
...
The replace / extend admin class code is taken from the official docs except this is all in one file.

Exclude URLs from Django REST Swagger

I have a few URLs that I want to exclude from my REST API documentation. I'm using Django REST Swagger and the only documentation I can find (https://github.com/marcgibbons/django-rest-swagger) doesn't really tell me much. There is the "exclude_namespaces" part of SWAGGER_SETTINGS in settings.py, but there is no real explanation or example of how to use this.
Simply put, I want to exclude any URLs from the docs that start with the following:
/api/jobs/status/
/api/jobs/parameters/
How could I go about doing this?
Thanks in advance for any help offered :P
the namespaces to exclude are the one defined in your urls.py.
So for example, in your case:
urls.py:
internal_apis = patterns('',
url(r'^/api/jobs/status/',...),
url(r'^/api/jobs/parameters/',...),
)
urlpatterns = urlpatterns + patterns('',
url(r'^', include(internal_apis, namespace="internal_apis")),
...
)
and in your settings.py:
SWAGGER_SETTINGS = {
"exclude_namespaces": ["internal_apis"], # List URL namespaces to ignore
}
This is well described in there
For all of those who found the above answer not helpful:
I guess that "exclude_namespaces" doesn't work anymore in new versions of django swagger. I had almost the same problem (I didnt't want to show my internal apis in documentation) and the above solution didn't work for me. I've been searching for like an hour for a solution and finally found something helpful.
There are some attributes that you can pass to SchemaGenerator. One of them is urlconf. You can set it to be "yourproject.api.urls" and it will get only urls defined there! Of course, you have to make sure that all the urls that you want to exclude from your api documentation are not included there.
I hope that at least one person found my answer helpful ;).
A problem comes when you want to have many urls.py included in your api documentation. I don't know what should be done then. If anyone comes up with an answer to this new problem - feel free to comment my answer. thanks!
With new version of django swagger, we don't need to create view to exclude some urls. Below code will disable test2 url.
from rest_framework_swagger.views import get_swagger_view
urlpatterns1 = [
url(r'^', include(router.urls)),
url(r'^test/', include('test.urls')),
url(r'^test1/', Test2.as_view()),
]
schema_view = get_swagger_view(title='API Documentation', patterns=urlpatterns1)
urlpatterns = urlpatterns1 + [
url(r'^docs/', schema_view),
url(r'^test2/', Test2.as_view()),
]
Ola's answer is correct. exclude_namespaces is no longer supported.
For finer control of the documentation, create your own schema view by using a function-based or class-based view. This can be useful if you want to produce documentation for specific URL patterns, or URL confs.
In your views.py, you can do the following:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.schemas import SchemaGenerator
from rest_framework_swagger import renderers
class SwaggerSchemaView(APIView):
renderer_classes = [
renderers.OpenAPIRenderer,
renderers.SwaggerUIRenderer
]
def get(self, request):
generator = SchemaGenerator(title='Your API Documentation', urlconf='your_app.urls')
schema = generator.get_schema(request=request)
return Response(schema)
The above will only render documentation for the URLs that are specified in the urlconf argument of the SchemaGenerator. Also, don't forget to set up your urls.py as well:
from django.conf.urls import url
from views import SwaggerSchemaView
urlpatterns = [
url(r'^api/v1/docs/$', SwaggerSchemaView.as_view(), name='docs'),
]
For the newest version of drf-swagger you can defile url patterns in the schema generator.
For example:
url_patterns = (
url(r'^api/v1/', include(router.urls, namespace='api')),
)
generator = schemas.SchemaGenerator(title='Core API', patterns=url_patterns)
A more flexible solution would be:
from django.contrib import admin
from django.urls import include, path
from rest_framework_swagger.views import get_swagger_view
urlpatterns = [
path('admin/', admin.site.urls),
path('users/', include('user.urls', namespace="user")),
path('locations/', include('location.urls')),
path('departments/', include('department.urls', namespace="department")),
path('my_secret_api/', include('secret.urls', namespace="secret_api")),
]
to_exclude = ['secret_api',] # some more namespaces here
swagger_urls = [item for item in urlpatterns if hasattr(item,"namespace") and item.namespace not in to_exclude]
schema_view = get_swagger_view(title='Highky', patterns=swagger_urls)
urlpatterns += [
path('api/docs/', schema_view),
]
urlpatterns will have all five paths, but swagger_urls will have four paths excluding secret_api.
All of your URLs and includes will continue to work as they were, except we are now passing our modified urlpatterns that we want to show in the Swagger docs. The checks will also cover the include where you don't specify a namespace (like in our case, where the namespace is not defined in the location).
views.py
any view class
class ...ViewSet(viewsets.ModelViewSet):
queryset = ....objects.all().order_by('-id')
serializer_class = ...Serializer
http_method_names = ['get', 'post', 'patch', 'delete'] # add or exclude
any function-based view
#api_view(['get']) # target field
def function(request):
...
return Response(...)

Django nested URLs

How do I nest url calls in django? For example, if I have two models defined as
class Post(models.Model):
title = models.CharField(max_length=50)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True, editable=False)
def __unicode__(self):
return self.title
#property
def comments(self):
return self.comment_set.all()
class Comment(models.Model):
comment = models.TextField()
post = models.ForeignKey(Post)
created = models.DateTimeField(auto_now_add=True)
With the following url files
root url
urlpatterns = patterns('',
url(r'^post/', include('post.urls')),
)
post url
urlpatterns = patterns('',
url(r'^$', views.PostList.as_view()),
url(r'^(?P<pk>[0-9]+)/$', views.PostDetail.as_view()),
url(r'^(?P<pk>[0-9]+)/comments/$', include('comment.urls')),
)
comment url
urlpatterns = patterns('',
url(r'^$', CommentList.as_view()),
url(r'^(?P<pk>[0-9]+)/$', CommentDetail.as_view()),
)
But when I go to /post/2/comments/1, I am given a Page not found error stating
Using the URLconf defined in advanced_rest.urls, Django tried these URL patterns, in this order:
^post/ ^$
^post/ ^(?P<pk>[0-9]+)/$
^post/ ^(?P<pk>[0-9]+)/comments/$
The current URL, post/2/comments/1, didn't match any of these.
This is not a problem though when I visit /post/2/comments Is this not allowed by django to have nested URL calls like this?
I think is probably because you're finishing the regex with the dollar sign $. Try this line without the dollar sign:
...
url(r'^(?P<pk>[0-9]+)/comments/', include('comment.urls')),
...
Hope it helps!
You have a $ at the end of r'^(?P<pk>[0-9]+)/comments/$'.
That means Django will only match with that URL when there is nothing after that.
So any longer URLs currently won't be considered. Therefore, you need to update the regular expression to:
url(r'^(?P<pk>[0-9]+)/comments/', include('comment.urls')),
Above Django 2.0 you can use simply...
urlpatterns = [
path('<pk>/comments/', include('comment.urls')),
]

Categories