django restframwork Put and DELETE doesn't work - django

Get and post work pretty well for me .
but put and delete i use "U_repo_name" to look in table
i got the error message :
Page not found (404)
Request Method: PUT
Request URL: http://localhost:8000/gitapi/repo/authoo/
Using the URLconf defined in gitit.urls, Django tried these URL patterns, in this order:
admin/
gitapi/ [name='gitapi']
gitapi/ repo/
gitapi/ repo/str:U_repo_name
this is my model :
class UserRepos(models.Model):
U_repo_name =models.CharField( max_length=50)
this is my url :
urlpatterns = [
path('repo/',view=userreposapi),
path('repo/<str:U_repo_name>',view=userreposapi),
]
project urls :
urlpatterns = [
path('admin/', admin.site.urls),
path('gitapi/',include('gitapi.urls')),
]
this is my serializer :
class UserReposSerializer(serializers.ModelSerializer):
class Meta:
model = UserRepos
fields ='__all__'
and this is my views :
#csrf_exempt
def userreposapi(request,id=0):
if request.method=='GET':
userrepos = UserRepos.objects.all()
userreposserializer = UserReposSerializer(userrepos , many=True)
return JsonResponse(userreposserializer.data , safe=False)
elif request.method=='POST':
userrepos_data=JSONParser().parse(request)
userreposerializer = UserReposSerializer(data=userrepos_data)
if userreposerializer.is_valid():
userreposerializer.save()
return JsonResponse("added successfully!!",safe=False)
return JsonResponse("failed to add",safe=False)
elif request.method=='Put':
userrepos_data=JSONParser().parse(request)
userrepos = UserRepos.objects.get(U_repo_name=userrepos_data['U_repo_name'])
userreposserializer=UserReposSerializer(userrepos,data=userrepos_data)
if userreposserializer.is_valid():
userreposserializer.save()
return JsonResponse("updated successfully", safe=False)
return JsonResponse("failed to update",safe=False)
elif request.method=='DELETE':
userrepos = UserRepos.objects.all()
userrepos.delete()
return JsonResponse("deleted",safe=False)

Hi! I see a few issues with your code that might be causing this problem.
request.method is always full capitalized, so you should use the string 'PUT' instead of 'Put' on this line:
elif request.method=='Put':
You are adding a trailing slash to your route when trying to access it (/gitapi/repo/authoo/), but the pattern set in urlpatterns doesn't have this trailing slash:
path('repo/<str:U_repo_name>',view=userreposapi),
You can use re_path() instead of path() to set a regex URL pattern that works whether you add a trailing slash or not:
re_path(r'repo/(?P<U_repo_name>\w+)/?', view=userreposapi),
(?P<U_repo_name>\w+) creates a named group called U_repo_name that matches any word character ([a-zA-Z0-9_]) from one to more times. /? matches a single optional /.
Your route 'repo/<str:U_repo_name>' captures a string in the variable U_repo_name. This variable is provided to the view userreposapi() as a keyword argument, but the view currently only accepts the id kwarg. You should either add the U_repo_name kwarg as an optional argument or make the view accept other keyword arguments with **kwargs:
# Both are valid:
def userreposapi(request, id=0, U_repo_name=None):
...
def userreposapi(request, id=0, **kwargs):
...
Fixing these 3 issues should make the route work.

I think the trailing slash in the url is the problem. I should be not there.
http://localhost:8000/gitapi/repo/authoo # here I removed the trailing slash.

Related

django - No User matches the given query. Page Not found 404: User post list view breaks post detail view

I'm fairly new to django and trying to build a message board app. Everything's been running smoothly up until I tried to add functionality to display a list of posts by author. I got it working but then my post detail view started throwing a 'No User matches the given query. Page Not found 404' error and I can't seem to figure out why. Can anyone help?
views.py
class UserPostList(generic.ListView):
model = Post
# queryset = Post.objects.filter(status=1).order_by('-created_on')
template_name = 'user_posts.html'
paginate_by = 6
def get_queryset(self):
"""
Method to return posts restricted to 'published' status AND to
authorship by the user whose username is the parameter in the
url.
"""
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(
status=1, author=user
).order_by('-created_on')
class FullPost(View):
def get(self, request, slug, *args, **kwargs):
"""
Method to get post object.
"""
queryset = Post.objects.filter(status=1)
post = get_object_or_404(queryset, slug=slug)
comments = post.comments.order_by('created_on')
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
return render(
request,
"full_post.html",
{
"post": post,
"comments": comments,
"liked": liked
},
)
# I'll be adding a comment form in here too
urls.py
urlpatterns = [
...
path('<slug:slug>/', views.FullPost.as_view(), name='boards_post'),
...
path('<str:username>/', views.UserPostList.as_view(), name='user_posts'),
...
]
Error message
(When trying to view a single post (previously working) after adding the UserPostList view and route)
Using the URLconf defined in mhcmsgboard.urls, Django tried these URL patterns, in this order:
1. admin/
2. summernote/
3. register/ [name='register']
4. profile/ [name='profile']
5. login/ [name='login']
6. logout/ [name='logout']
7. new/ [name='create_post']
8. <slug:slug>/update/ [name='update_post']
9. <slug:slug>/delete/ [name='delete_post']
10. <str:username>/ [name='user_posts']
The current path, test-post/, matched the last one.
for <str:name> in path "-" not allowed to use in name,
when you use Slug both paths are equal.
urlpatterns = [
...
path('<slug:slug>/', views.FullPost.as_view(), name='boards_post'),
...
path('<slug:username>/', views.UserPostList.as_view(), name='user_posts'),
]
there are 2 simple ways
use sub path for one or both paths : user_post/<slug:username>/
Use re_path and define the regex to change path
exp:
re_path(r'^(?P\<username>\w+)/$', views.UserPostList.as_view()),
The problem is that you match update_post, delete_post and user_posts URL's to the root. As the error message explains, the current request is made against user_posts. And it seems that you don't have a user called test-post.
You could solve it e.g. with the following URL conf:
urlpatterns = [
...
path('board/<slug:slug>/', views.FullPost.as_view(), name='boards_post'),
...
path('posts/<str:username>/', views.UserPostList.as_view(), name='user_posts'),
...
]
That way, each path is unique and Django knows which View it has to call.

How to perform a query by using URL with question mark in Django?

It seems like the original URL querying function has been removed from Django 3.1. Does anyone know how to do it with a new package?
The url.py:
urlpatterns = [
re_path(r'^portfolio/(?P<title>[\w-]+)/$' , BlogApp_View.displayPortfolio, name='displayPortfolio'),
path('portfolio/', BlogApp_View.selectPortfolio, name='selectPortfolio'),]
The view.py
def displayPortfolio(request):
title = request.GET.get('title')
portfolio = Article.objects.filter(articleType__name__contains = "Portfolio", title=title)
print(title)
DICT = {}
return render(request, 'Article/', DICT)
The problem is now if I visit http://127.0.0.1:8000/Blog/portfolio/?title=A_UAV_Positioning_Approach_Using_LoRa/, it will skip the re_path shows in url.py.
Instead, it goes to the path one.
I have tried str:title method but that is actually not what I want. I prefer using the question mark pattern to finish the query.
The part after the questionmark is the querystring [wiki] and is not part of the path. This thus means that regardless what patterns you write, you can not distinguish on this, since the path patterns, regardless whether it is a path or re_path, are never matched against a URL with a query string.
You thus should write a single view, and inspect the request.GET query dict (which is a dictionary-like representation of the query string and see if it contains a value for title.
Your urlpatterns thus look like:
urlpatterns = [
path('portfolio/', BlogApp_View.selectPortfolio, name='selectPortfolio'),
]
and in the view, you can see if it contains a title:
def selectPortfolio(request):
if 'title' in request.GET:
# contains a ?title=…
title = request.GET.get('title')
portfolio = Article.objects.filter(
articleType__name__contains='Portfolio',
title=title
)
data = {'portfolio': portfolio}
return render(request, 'some_template.html', data)
else:
# contains no ?title=…
# …
return …

Most appropriate way to redirect page after successful POST request in Django

I have build a view and a form in Django1.5. If the POST request is successful (based on some values I set) then I need the page to redirect to another URL which is created simultaneously.
Otherwise, if the POST was not successful I need to stay on the same page. Right now I have solved the problem as following but I am quite sure this is not the best way to do it:
This is a part of my view:
def layer_create(request, template='layers/layer_create.html'):
if request.method == 'GET':
....
elif request.method == 'POST':
out = {}
...
new_table = 'something that comes from the form'
if form.is_valid():
...
try:
...
out['success'] = True
except:
...
out['success'] = False
finally:
if out['success']:
status_code = 200
# THIS IS THE PART WHICH I THINK I CAN IMPROVE
template = '/something/workspace:' + new_table + '/metadata'
else: # if form not valid
out['success'] = False
return render_to_response(template, RequestContext(request, {'form': form}))
This part of the code:
template = '/something/workspace:' + new_table + '/metadata'
seems very ugly to me. But as I am quite new in Django I am not sure how to approach this matter.
A side note first about Django 1.5 - you're highly advised to upgrade to a supported version like 1.8.
Redirecting
For redirecting you can use the redirect shortcut. (Or HttpResponseRedirect)
from django.shortcuts import redirect
# out of a view context
return redirect('/url/to/redirect/to/')
Building URLs
Indeed - as you did mention, your attempt with template = '/something/workspace:' + new_table + '/metadata' is not the cleanest way :)
Django provides a really nice way with the URL dispatcher.
A complete solution here would go too far (or definitely would require more detailed information about your project structure) - I would recommend you to dive into the Django URL dispatcher.
In short you would do something like:
# app/urls.py
urlpatterns = [
#...
url(r'^workspace/(?P<id>[0-9]+)/metadata/$', views.workspace_detail, name='workspace-detail-metadata'),
#...
]
Then you are able to reverse your URL patterns:
from django.core.urlresolvers import reverse
url = reverse('workspace-detail-metadata', kwargs={'id': 123})
# would result in:
# .../workspace/123/metadata/
After all, I have used the "reverse" method as follows:
layer = 'geonode:' + new_table
return HttpResponseRedirect(
reverse(
'layer_metadata',
args=(
layer,
)))
Where my urls.py file includes:
url(r'^(?P<layername>[^/]*)/metadata$', 'layer_metadata', name="layer_metadata"),
As described here this is the most appropriate way to do it.

Django NoReverseMatch at /search/

In a search form, users can choose some of the criteria (country, province and city) and post it the result to this view:
def profile_search(request):
if request.method == 'POST':
form = AdvancedSearchForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
country=cd['country']
province=cd['province']
city = cd['city']
params=( country, province, city ,)
url = reverse('userprofile.views.profile_search_result', args= params)
return HttpResponseRedirect(url)
args = {}
args.update(csrf(request))
return HttpResponseRedirect("/result/ایران")
Which is supposed to be found by one of these url patterns:
url(r'^result/(?P<country>\w+)/(?P<province>\w+)/(?P<city>\w+)', 'userprofile.views.profile_search_result'),
url(r'^result/(?P<country>\w+)/$','userprofile.views.profile_search_result'),
However, no matter what criteria are chosen in the search form, I get errors like:
Reverse for 'userprofile.views.profile_search_result' with arguments '(u'\u0627\u06cc\u0631\u0627\u0646', u'\u0627\u0644\u0628\u0631\u0632', u'')' and keyword arguments '{}' not found. 2 pattern(s) tried: ['result/(?P<country>\\w+)/$', 'result/(?P<country>\\w+)/(?P<province>\\w+)/(?P<city>\\w+)']
How to fix this?
You have two problems.
Firstly, \w does not match the Arabic characters you are using in your params - it's equivalent to [A-Za-z0-9] only. You will need to explicitly use the Unicode codepoints for the characters you want to match, or alternatively a much more general .:
r'^result/(?P<country>.+)/(?P<province>.+)/(?P<city>.+)'
Secondly, in your pattern the city parameter is required, but you are passing an empty string. You should probably define a third pattern that omits that parameter:
r'^result/(?P<country>.+)/(?P<province>.+)'
But note that I don't recommend doing any of that. Rather than submitting your form as a POST and redirecting to a second view with URL parameters to actually do the search, you should simply use a GET action in your form and submit it straight to the search_result view, where you get the search query from request.GET.
try this line in urls.py
url(r'^result/(?P<country>.*)/$','userprofile.views.profile_search_result'),
this is sample of using unicode chars in my application:
urls.py
urlpatterns += patterns('pin.views',
url(r'^(?P<user_namefl>.*)/followers/$', 'absuser_followers', name='pin-absuser-followers'),
url(r'^(?P<user_namefg>.*)/following/$', 'absuser_friends', name='pin-absuser-following'),
url(r'^(?P<user_namel>.*)/likes/$', 'absuser_like', name='pin-absuser-like'),
url(r'^(?P<user_name>.*)/$', 'absuser', name='pin-absuser'),
)
links from html pages
<a href="{% url "pin-absuser" username %}">
with reverse in views or models
reverse("pin-absuser", args=["وحید"])
To use reverse(), you need to give the url a name, and use it in the reverse function - see documentation.
Example in urls.py:
url(r'^result/(?P<country>\w+)/(?P<province>\w+)/(?P<city>\w+)', 'userprofile.views.profile_search_result', name='search_results'),
Example call:
url = reverse('search_results', args= params)

How to avoid Django URLField adding the trailing slash?

Django URLField likes to add a trailing slash (/) at the end of the user input, forcing all URLs to be stored with the extra character, this is wrong. How can I stop this behavior and save URLs as submitted by users?
Check to_python of URLField at https://github.com/django/django/blob/master/django/forms/fields.py.
You can see it has a line url_fields[2] = '/' almost at the end of method to_python. It appends a trailing slash / at the end of url. You can see the logic for doing this as a comment before this line.
This slash is necessary in case some query params are given.
If you want to avoid this behaviour, write you own field which extends from URLField and override to_python in your custom class.
I've been struggling with this as well, because it's causing a problem for certain urls. For example, http://www.nasa.gov/mission_pages/kepler/news/kepler-62-kepler-69.html/ fails, but it works without the slash.
To expand on akshar's answer, the method to do this is explained here. For example, defining this in my models.py file and setting url = NoSlashURLField() rather than models.URLField() in my model removes the slash:
try:
from urllib.parse import urlsplit, urlunsplit
except ImportError: # Python 2
from urlparse import urlsplit, urlunsplit
class NoSlashURLField(models.URLField):
description = "Remove the goddamn slash"
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
super(NoSlashURLField, self).__init__(*args, **kwargs)
def to_python(self, value):
def split_url(url):
"""
Returns a list of url parts via ``urlparse.urlsplit`` (or raises a
``ValidationError`` exception for certain).
"""
try:
return list(urlsplit(url))
except ValueError:
# urlparse.urlsplit can raise a ValueError with some
# misformatted URLs.
raise ValidationError(self.error_messages['invalid'])
value = super(NoSlashURLField, self).to_python(value)
if value:
url_fields = split_url(value)
if not url_fields[0]:
# If no URL scheme given, assume http://
url_fields[0] = 'http'
if not url_fields[1]:
# Assume that if no domain is provided, that the path segment
# contains the domain.
url_fields[1] = url_fields[2]
url_fields[2] = ''
# Rebuild the url_fields list, since the domain segment may now
# contain the path too.
url_fields = split_url(urlunsplit(url_fields))
# if not url_fields[2]:
# # the path portion may need to be added before query params
# url_fields[2] = '/'
value = urlunsplit(url_fields)
return value
For those using the usual Django admin forms for their site, and also using South for DB migrations, you may want to use the following method instead of stonefury's. His method changes the model field, which confuses South unless you add some special code. The below method changes only the admin code, allowing South to remain blissfully unaware.
Define this class somewhere in your app:
class NoSlashURLFormField(forms.URLField):
def to_python(self, value):
def split_url(url):
"""
Returns a list of url parts via ``urlparse.urlsplit`` (or raises a
``ValidationError`` exception for certain).
"""
try:
return list(urlsplit(url))
except ValueError:
# urlparse.urlsplit can raise a ValueError with some
# misformatted URLs.
raise ValidationError(self.error_messages['invalid'])
if value:
url_fields = split_url(value)
if not url_fields[0]:
# If no URL scheme given, assume http://
url_fields[0] = 'http'
if not url_fields[1]:
# Assume that if no domain is provided, that the path segment
# contains the domain.
url_fields[1] = url_fields[2]
url_fields[2] = ''
# Rebuild the url_fields list, since the domain segment may now
# contain the path too.
url_fields = split_url(urlunsplit(url_fields))
value = urlunsplit(url_fields)
return value
Then edit your admin.py file as follows:
from your_app.path.to.noslash import NoSlashURLFormField
from django.contrib.admin.widgets import AdminURLFieldWidget
class MyModelAdmin(admin.ModelAdmin):
...
formfield_overrides = {
models.URLField: {
'form_class': NoSlashURLFormField,
# Need to specify the AdminURLFieldWidget here because it would
# otherwise get defaulted back to URLInput.
'widget': AdminURLFieldWidget,
}
}