I rather want to use Django's built-in functionalities as much as possible and avoid implementing stuff myself as much as possible!
Why doesn't the following code issue exceptions when given a non-URL value?
models.py:
from django.core.validators import URLValidator
from django.db import models
class Snapshot(models.Model):
url = models.URLField(validators=[URLValidator])
views.py:
from django.http import HttpResponse
from .models import Snapshot
def index(request):
a = Snapshot(url='gott ist tot')
a.save()
Because this validator is run when you use a django form.
More information about validators on the doc : https://docs.djangoproject.com/en/4.1/ref/validators/
if you do a form :
from django import forms
from .models import Snapshot
class SnshotForm(forms.ModelForm):
class Meta:
model = Snapshot
fields = ('url', )
and your views.py :
from django.http import HttpResponse
from .forms import SnapshotForm
def index(request):
a = SnapshotForm(data={'url': 'gott ist tot'})
if a .is_valid()
a.save()
else:
print(a.errors)
Your validator will be run and you will see the errors form message
Without using form, you can call manually validator in your view or in the save method:
# in views
from django.core.validators import URLValidator
from django.core.exceptions import ValidationError
def index(request):
url_validator = URLValidator()
url = 'gott ist tot'
is_valid_url = False
try:
url_validator(url)
except ValidationError:
pass
if is_valid_url:
a = Snapshot(url=url)
a.save()
else:
print(a.errors)
Be careful ! I do not recommanded to bypass the validator with forms, i think it is the better way for maximizing usage of django builtins funtions
I have a regular ModelViewSetin my project, and it works perfectly with GET and POST requests, but it fails with PUT, returning this error:
{
"detail": "CSRF Failed: CSRF token missing or incorrect."
}
This is my urls.py:
from django.urls import path,re_path,include
from django.utils.text import slugify,camel_case_to_spaces
from PaymentsManagerApp import views, models
from rest_framework import routers
APP_NAME = 'PaymentsManagerApp'
router = routers.DefaultRouter()
router.register(r'payments', views.PaymentViewSet)
payments_list = views.PaymentViewSet.as_view({
'get':'list',
'post':'create'
})
payment_detail = views.PaymentViewSet.as_view({
'get':'retrieve',
'put':'update',
'patch':'partial_update',
'delete':'destroy'
})
def urlpattern_from_route(route):
if "regex" in route and route['regex']:
path_method = re_path
else:
path_method = path
return path_method(route['path'],route['view'].as_view(),name=route['name'] if "name" in route else None)
routes_views = list(map(urlpattern_from_route,routes))
route_services = [
payment_detail = views.PaymentViewSet.as_view({
'get':'retrieve',
'put':'update',
'patch':'partial_update',
'delete':'destroy'
})
route_services = [
path('payments/', payments_list, name='rest_payments_list'),
path('payments/<int:pk>/', payment_detail, name='rest_payment_detail'),
]
urlpatterns = routes_views + route_services
This is my views.py:
import os
import json
from datetime import datetime, timedelta
from django.shortcuts import render
from PaymentsManagerApp import urls, models, serializers
from FrontEndApp import urls as Fronturls
from django.shortcuts import render,redirect
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.views.generic import View
from django.contrib.auth.models import Permission
from GeneralApp.utils import get_catalogs
from django.contrib.staticfiles import finders
from django.utils.text import slugify,camel_case_to_spaces
from rest_framework import viewsets, permissions
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.response import Response
from rest_framework.filters import OrderingFilter, SearchFilter
from django.db.models import Q
class PaymentViewSet(viewsets.ModelViewSet):
exclude_from_schema = True
permission_classes = (permissions.IsAuthenticated,)
queryset = models.Payment.objects.all()
serializer_class = serializers.PaymentSerializer
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter,)
search_fields = ('payment_type', 'creation_user__username', 'provider__name', 'invoice', 'payment_method_type', 'payment_document_number')
filter_fields = ('id', 'payment_type', 'creation_user', 'provider', 'is_payment_requested', 'is_paid', 'payment_method_type')
When I send a GET or a POST to payments_manager/payments/, it works perfectly. Also when I send a GET to pyments_manager/payments/<int:pk>/it works well.
The problem is when I send a PUT to payments_manager/payments/<int:pk>/, because I get the following:
I don't know why but DRF loses the logged user information (you can see the log in label, instead of the username).
EDIT
This is my REST_FRAMEWORK in settings.py:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 20,
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata'
}
EDIT
I found that the error rises only if I access the endpoint directly from the browser using DRF default interfase (127.0.0.1:8000/es/payments_manager/payments/1/):
My PUT requests work perfectly from my javascript ajax.
In order for to use session auth AND do a POST (is a little weird) / PUT / PATCH / DELETE / etc. you MUST pass a header.
see: https://github.com/django/django/blob/8b3f1c35dd848678225e8634d6880efeeab5e796/django/middleware/csrf.py#L306
I also created a little test for you:
https://gist.github.com/kingbuzzman/20dffbc34d22a899661ac3c065e3f747#file-django_rest_framework_session_vs_token-py-L209
response = self.client.post('/session-login/', data={'username': 'user', 'password': 'pass'})
self.assertEqual(302, response.status_code)
self.assertIn('csrftoken', response.cookies)
self.assertIn('sessionid', response.cookies)
# Don't want to go through the trouble of having to get the CSRF from the login form
self.client.handler.enforce_csrf_checks = True
csrftoken = self.client.cookies.get('csrftoken').value
# NOTE: The only reason this works it's because we're passing a header along with the request.
response = self.client.patch('/payments/%s/' % (self.payment.id), content_type='application/json',
data=json.dumps({'is_paid': 'Y'}), HTTP_X_CSRFTOKEN=csrftoken)
self.assertEqual(200, response.status_code)
self.assertEqual('Y', response.json()['is_paid'])
# NOTE: The reason this DOES NOT works it's because we're NOT passing a header along with the request.
response = self.client.patch('/payments/%s/' % (self.payment.id), content_type='application/json',
data=json.dumps({'is_paid': 'N'}))
self.assertEqual(403, response.status_code)
Edit.
Added a user, logged in, navigated to localhost/payments/ and added a record, then went to the record localhost/payments/1/ and updated it (PUT). Everything works. Please add your django/drf versions.
I was working on a project where the user inputs the answer to the question and then the user input needs to be checked on clicking submit button. If input matches the answer stored in database in the admin portal then it should redirect it to a new page else it should give an error wrong answer.
What's happening is that for every user input i am redirected to the other page. But never shows Wrong Answer even if input other than that in database answer is entered. How do I tackle this?
forms.py
from django import forms
from .models import Answer
from django.core.exceptions import ObjectDoesNotExist
class CheckAnswer(forms.Form):
your_answer=forms.CharField(label='Answer')
def clean(self):
cleaned_data=super(CheckAnswer,self).clean()
response=cleaned_data.get("your_answer")
try:
p = Answer.objects.filter(answer__contains=response)
except Answer.DoesNotExist:
raise forms.ValidationError("Wrong Answer")
models.py
from django.db import models
from django.contrib.auth import get_user_model
User=get_user_model()
users=User.objects.all()
class Answer(models.Model):
name=models.CharField(max_length=10,unique=True)
answer=models.CharField(max_length=100)
def __str__(self):
return self.name
class Meta:
ordering= ["-name"]
views.py
from django.shortcuts import render,redirect
from django.views.generic import *
from . import models
from django import forms
from .forms import CheckAnswer
def Arena1(request):
if request.method=='POST':
form = CheckAnswer(request.POST)
if form.is_valid():
return redirect('thanks')
else:
form=CheckAnswer()
return render(request,'levels/arena1.html',{'form':form})
The Model Managers filter method does not raise DoesNotExist, you should use Answer.objects.get(answer__contains=response) to raise DoesNotExist, keep in mind that this can also return a MultipleObjectsReturned error if there is more than one answer matching your response, filter will just return an empty list.
The other option is to use Answer.objects.filter(answer__contains=response).exists() and check if it returns true or false.
I'm successfully using a custom user model with django.
The last thing to get working is the "AdminChangePasswordForm" for superusers to change any users password.
currently the change password link from admin:myapp:user gives a 404
The answer.
Override get_urls
and override UserChangeForm to have the correct url.
So I had similar problem. When I tried to change user password from admin I got url to "/admin/accounts/siteuser/password/" (siteuser is the name of my custom user model) and 404 error with this message: "user object with primary key u'password' does not exist."
The investigation showed that the problem was due to bug in django-authtools (1.4.0) as I used NamedUserAdmin class to inherit from.
So the solution is either (if you need to inherit from any custom UserAdmin like NamedUserAdmin from django-authtools):
from django.contrib.auth.forms import UserChangeForm
from authtools.admin import NamedUserAdmin
class SiteUserAdmin(NamedUserAdmin):
...
form = UserChangeForm
...
or just inherit from default django UserAdmin:
from django.contrib.auth.admin import UserAdmin
class SiteUserAdmin(UserAdmin):
pass
It seems it's a "bug" in 1.7.x, and fixed in 1.8.x, which set the url name, so you do have to override get_urls():
from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
from django.conf.urls import url
class UserAdmin(AuthUserAdmin):
def get_urls(self):
return [
url(r'^(.+)/password/$', self.admin_site.admin_view(self.user_change_password), name='auth_user_password_change'),
] + super(UserAdmin, self).get_urls()
URL:
password_change_url = urlresolvers.reverse('admin:auth_user_password_change', args=(1,))
I want the user query in my Django/Haystack app to be sorted in alphabetical order. For example if the user entered "sit" this would be changed to "ist" in the Haystack Search Index.
I believe overriding the default Haystack forms.py will achieve this, but can't get it to work.
#forms.py
from haystack.forms import SearchForm
class WordsSearchForm(SearchForm):
q_default = forms.CharField(required=False, label=_('Search'),
widget=forms.TextInput(attrs={'type': 'search'}))
q = ''.join(sorted(q_default))
Any suggestions on how to achieve this?
Customizing Haystack is a bit tricky it turns out. To override the user query you need to create a forms.py file, update views.py, and update urls.py.
#forms.py
from haystack.forms import SearchForm
class WordsSearchForm(SearchForm):
def search(self):
# added the two lines below
q = self.cleaned_data['q']
q = ''.join(sorted(q))
if not self.is_valid():
return self.no_query_found()
if not self.cleaned_data.get('q'):
return self.no_query_found()
# default is...
# sqs = self.searchqueryset.auto_query(self.cleaned_data['q'])
sqs = self.searchqueryset.auto_query(q)
if self.load_all:
sqs = sqs.load_all()
return sqs
# views.py
from haystack.views import SearchView
from .models import [NAME OF YOUR MODEL]
from .forms import WordsSearchForm
class MySearchView(SearchView):
form = WordsSearchForm
#urls.py
from haystack.views import SearchView, search_view_factory
from scrabble.views import MySearchView
from APP.forms import WordsSearchForm
urlpatterns = patterns('',
# replace default haystack url
# url(r'^search/', include('haystack.urls')),
url(r'^search/$', search_view_factory(
form_class=WordsSearchForm
), name='haystack_search'),
)