I've installed django-registration-redux and custom_user. I have a UserProfile model with fullname,dob and photo as extra fields.
I have connected the UserProfile with user-registration so as to save the additional fields to UserProfile table.
Here my questions are...
How do I validate fields in UserProfile model at the time of registration so that I can prevent addition of a user to the emailuser table. Say, if the dob given is invalid or not in allowed range, then stop adding a user to emailuser table.
How can I make an EditProfile system that allows authenticated users to edit their profile (/user/edit - id to be obtained from session).
Below is my settings.py file
INSTALLED_APPS = [
'django.contrib.admin',
'custom_user',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
#------------
'registration',
'userprofile',
]
AUTH_USER_MODEL = 'custom_user.EmailUser'
models.py
class UserProfile(models.Model):
user=models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
primary_key=True,)
fullname=models.CharField(max_length=70, blank=False)
dob=models.DateField(blank=False)
photo=models.ImageField(upload_to='profile_images', blank=False)
def __str__(self):
return self.user.email
def user_registered_callback(sender, user, request, **kwargs):
profile = UserProfile(user = user)
profile.fullname =request.POST["fullname"]
profile.dob ="%s-%s-%s" % (request.POST["dob_year"],request.POST["dob_month"],request.POST["dob_day"])
if 'photo' in request.FILES:
profile.photo = request.FILES['photo']
profile.save()
user_registered.connect(user_registered_callback)
forms.py
class UserProfileForm(RegistrationForm):
fullname=forms.CharField(required=True,label="Full name", min_length=3, max_length=75)
dob=forms.DateField(required=True,label="Date of birth",
widget=forms.SelectDateWidget(years=range(now.year-settings.AGE_UPPER,now.year-settings.AGE_LOWER)))
photo=forms.ImageField(required=False)
views.py
def profile(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/user/login')
else:
return render(request, 'userprofile/profile.html', {"user":request.user})
class EditProfile(UpdateView):
template_name = 'userprofile/edit_profile.html'
fields = ['fullname','dob','photo']
main urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'user/register/$',
RegistrationView.as_view(form_class = UserProfileForm),
name = 'registration_register'),
url(r'^user/', include('registration.backends.default.urls')),
url(r'^user/', include('userprofile.urls')),
userprofile.urls.py
urlpatterns = [
url(r'^profile/$', views.profile, name='profile'),
url(r'^profile/edit/$', views.EditProfile.as_view(), name='edit_profile'),
]
Right now I'm, getting EditProfile is missing a QuerySet. Define EditProfile.model, EditProfile.queryset, or override EditProfile.get_queryset().
Is it the right way to proceed? Any help would be appreciated.
Thanks
Related
I'm creating API for Twitter like app in Django and since I implemented token authentication (rest-auth) I have problem with creating new tweets as it throws:
{
"author": [
"This field is required."
]
}
I've tried CreateAPIView:
class TweetCreateAPIView(CreateAPIView):
serializer_class = TweetModelSerializer
permission_classes = (IsAuthenticated,)
# I've also tried to add the author field mannualy
def perform_create(self, serializer):
serializer.save(author=self.request.user)
but it didn't work so I created custom post method:
class TweetCreateAPIView(APIView):
permission_classes = (IsAuthenticated,)
def post(self, request, format=None):
serializer = TweetModelSerializer(data=request.data)
if serializer.is_valid():
serializer.save(author=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
but it still can't identify the user creating the tweet
Model:
class Tweet(models.Model):
retweeted_by = models.ManyToManyField(
TwitterUser, blank=True, related_name='retweets')
commented_tweet = models.ManyToManyField(
'self', related_name='comments', blank=True, symmetrical=False)
author = models.ForeignKey(
TwitterUser, on_delete=models.CASCADE, related_name='tweets')
content = models.CharField(max_length=280)
created_at = models.DateTimeField(auto_now_add=True)
liked_by = models.ManyToManyField(
TwitterUser, blank=True, related_name='likes')
objects = TweetManager()
def __str__(self):
return self.content
def get_comments(self):
comments = Tweet.objects.filter(commented_tweet__pk=self.pk)
return comments
def get_retweets(self):
retweets = TwitterUser.retweets(tweet__id=self.id)
def get_absolute_url(self):
return reverse('tweets:pk', kwargs={'pk': self.pk})
Serializer:
class TweetModelSerializer(serializers.ModelSerializer):
likes_count = serializers.SerializerMethodField()
already_liked = serializers.SerializerMethodField()
def get_likes_count(self, tweet):
return tweet.liked_by.all().count()
def get_already_liked(self, tweet):
user = None
request = self.context.get("request")
if request and hasattr(request, "user"):
user = request.user
if user is not None:
if user in tweet.liked_by.all():
return True
else:
return False
else:
pass
class Meta:
model = Tweet
fields = [
'id',
'author',
'commented_tweet',
'content',
'retweeted_by',
'likes_count',
'already_liked',
'created_at',
]
Settings:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 3rd party
'rest_framework',
'rest_framework.authtoken',
'rest_auth',
'django.contrib.sites',
'channels',
'allauth',
'allauth.account',
'rest_auth.registration',
'reset_migrations',
'corsheaders',
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PAGINATION_CLASS': 'twitter.paginations.StandardResultsSetPagination',
'TEST_REQUEST_DEFAULT_FORMAT': 'json',
}
Authentication itself works fine, including registration, so I don't think that it's a problem with tokens, yet I have no other idea where the bug is when even CreateAPIView doesn't work.
In your serializer you have author in the list of fields. Since it's a ModelSerializer, drf is picking up details for that field from the model, and in the model author is null = False by default, hence drf is making it a required field, since it can't be null. But since you want the author to be automatically the user who is making the request, you don't need that field to be editable. Hence make it a read only field in your serializer like this:
class TweetModelSerializer(serializers.ModelSerializer):
...
class Meta:
model = Tweet
fields = ('id', 'author', 'commented_tweet', 'content', 'retweeted_by',
'likes_count', 'already_liked', 'created_at', )
read_only_fields = ('author', ) # it's read only now, so drf go looking for it in POST data
Now you just need to override the perform_create method, no need to change the post method.
If you need even more customizations like superuser can edit authors but no one else, you can override the __init__ method your serializer and make fields read_only based on request.user which gets a bit complicated.
Also, you should consider using tuple instead of list for fields and read_only_fields to get minor performance boost.
I'm trying to customize auth backend while customized auth model but keep facing this error because i'm using get_user_model() function.
django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'base.User' that has not been installed
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'base.apps.BaseConfig',
'core.apps.AccountsConfig',
'system.apps.SystemConfig',
]
custom Backend:
class UserBackend(object):
def authenticate(self, request, username=None, password=None, **kwargs):
usermodel = User
try:
usr = usermodel.objects.get(username=username)
password_valid = usr.check_password(password)
if usr and password_valid:
return usr
raise PermissionDenied
except usermodel.DoesNotExist:
return PermissionDenied
return None
def get_user(self, user_id):
usermodel = User
try:
return usermodel.objects.get(pk=user_id)
except usermodel.DoesNotExist:
return None
Edit:
settings:
AUTH_USER_MODEL = 'base.User'
AUTHENTICATION_BACKENDS = (
'base.models.UserBackend',
)
base.User model:
class User(AbstractUser):
fullname = models.CharField(max_length=35, null=True, blank=True)
picture = models.ManyToManyField('ImageFile', verbose_name="ProfilePicture", blank=True)
bio = models.CharField(max_length=255, null=True, blank=True)
link = models.URLField(null=True, blank=True, default="")
is_private = models.BooleanField(default=False)
is_official = models.BooleanField(default=False)
Note: UserBackend is on the end of file and class User(AbstractUser) is above it
There was an import in base.models file, from django.contrib.auth.backends import ModelBackend which caused this error even when i removed custom AUTHENTICATION_BACKENDS.after i removed this import, everything works fine although i moved backend class from base.models to backend file in the base app (i think its not necessary, i just did it for more readable codes)
For me it was the same. It took me over an hour to find out that you cannot have the CustomBackend(BaseBackend) and the CustomUser(AbstractUser) in the models.py of your app. This info is nowhere to be found in the official Django docs.
Django Version: 3.1.2
Python Version: 3.8.2
models.py of the app "Users":
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class User(AbstractUser):
last_name = models.CharField(max_length=50)
first_name = models.CharField(max_length=50)
auth.py (arbitrary name, living in the "Users" app):
from django.db import models
from django.contrib.auth.backends import BaseBackend
class UserAuth(BaseBackend):
def authenticate(self, request, username, password):
pass
def get_user(self, user_id):
pass
settings.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users.apps.UsersConfig'
]
AUTH_USER_MODEL = 'users.User'
AUTHENTICATION_BACKENDS = [
'users.auth.UserAuth'
]
I am trying to add object level permission to my django REST project using django-guardian, but I am getting
http://127.0.0.1:8000/api/v1/tasks/
HTTP 403 Forbidden
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"detail": "You do not have permission to perform this action."
}
The user joe is logged in.
settings.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'guardian',
'rest_framework',
'rest_framework.authtoken',
'rest_auth',
'task.apps.TaskConfig',
]
models.py:
class Task(models.Model):
summary = models.CharField(max_length=32)
content = models.TextField()
reported_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
permissions = (
('view_task', 'View task'),
)
serializers.py:
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = '__all__'
permissions.py:
class CustomObjectPermissions(permissions.DjangoObjectPermissions):
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
'HEAD': ['%(app_label)s.view_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
filters.py:
class DjangoObjectPermissionsFilter(BaseFilterBackend):
perm_format = '%(app_label)s.view_%(model_name)s'
shortcut_kwargs = {
'accept_global_perms': False,
}
def __init__(self):
assert 'guardian' in settings.INSTALLED_APPS, (
'Using DjangoObjectPermissionsFilter, '
'but django-guardian is not installed.')
def filter_queryset(self, request, queryset, view):
from guardian.shortcuts import get_objects_for_user
user = request.user
permission = self.perm_format % {
'app_label': queryset.model._meta.app_label,
'model_name': queryset.model._meta.model_name,
}
return get_objects_for_user(
user, permission, queryset,
**self.shortcut_kwargs)
views.py:
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all()
serializer_class = TaskSerializer
permission_classes = (CustomObjectPermissions,)
filter_backends = (DjangoObjectPermissionsFilter,)
urls.py:
router = DefaultRouter()
router.register('tasks', TaskViewSet, base_name='tasks')
urlpatterns = router.urls
But it works fine in shell
> python manage.py shell -i ipython
In [1]: from django.contrib.auth.models import User
In [2]: joe = User.objects.all().filter(username="joe")[0]
In [3]: import task.models as task_models
In [4]: task = task_models.Task.objects.all()[0]
In [5]: joe.has_perm('view_task', task)
Out[5]: True
The API first checks model-level permissions, then object-level permissions if they apply. Since the custom permissions class requires the user to have read-permissions, you need to ensure that Joe has been assigned model-level read access. If you check joe.has_perm('tasks.view_task'), I would bet that it returns False. To fix this, you either need to directly assign his user the permission, or add him to a group that has been assigned the appropriate permissions.
Also, note that Django 2.1 recently added the "view" permission, and it shouldn't be necessary to add it to your models anymore.
I have an issue with my authentication process.
I create a SignIN process giving a user the ability to create an a new account using only his mail, account that will be inactive until he edits it providing his first name, last name and a password.
I gave it a try but I go to the URL for edit profile I receive an error related to the PK ..
error:
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/registration/users/2/edit/
Django Version: 1.11.3
Python Version: 3.6.2
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'registration']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "/Users/raphaelbendenoun/anaconda/envs/myDjangoEnv/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
41. response = get_response(request)
File "/Users/raphaelbendenoun/anaconda/envs/myDjangoEnv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
187. response = self.process_exception_by_middleware(e, request)
File "/Users/raphaelbendenoun/anaconda/envs/myDjangoEnv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
185. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/raphaelbendenoun/Documents/testregis/registration/views.py" in update_profile
41. user = get_object_or_440(User, pk=pk) #find user instance by id in db
Exception Type: NameError at /registration/users/2/edit/
Exception Value: name 'get_object_or_440' is not defined
'pk'
urls:
from django.conf.urls import url, include
from registration import views
app_name = 'registration'
urlpatterns = [
url(r'^register/$', views.register, name='register'),
url(r'^users/(?P<pk>\d+)/edit/$', views.update_profile, name="edit-user-profile"),
]
form.py:
from django import forms
from django.contrib.auth.models import User
from .models import MyUser
from django.contrib.auth import get_user_model
User = get_user_model()
class Form(forms.ModelForm):
password = forms.CharField(widget= forms.PasswordInput())
class Meta():
model = User
fields= ('first_name','last_name','email','password','company')
class InactiveForm(forms.ModelForm):
class Meta():
model = User
fields= ('email',)
class UpdateProfile(forms.ModelForm):
email = forms.EmailField(required=True)
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
password = forms.CharField(widget= forms.PasswordInput())
class Meta:
model = User
fields = ('email', 'first_name', 'last_name','password')
def clean_email(self):
email = self.cleaned_data.get('email')
if email and User.objects.filter(email=email).count():
raise forms.ValidationError('This email address is already in use. Please supply a different email address.')
return email
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
views.py
from django.shortcuts import render
from django.views import generic
from django.views.generic import TemplateView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from .forms import InviteForm
from invitations.models import Invitation
from .models import project
from django.core.urlresolvers import reverse
# Create your views here.
class HomePage(TemplateView):
template_name= 'index.html'
class CandidateIndex(TemplateView):
template_name= 'candidateIndex.html'
class HRIndex(TemplateView):
template_name= 'HRindex.html'
class ProjectDetailView(generic.DetailView):
model = project
template_name = 'project_details.html'
class ProjectCreate(CreateView):
model = project
fields = ['project_name']
template_name = 'project_form.html'
def create_invite(request):
if request.method == "POST":
invite_form = InviteForm(data=request.POST)
if invite_form.is_valid():
email1 = invite_form.cleaned_data['email1']
email2 = invite_form.cleaned_data['email2']
email3 = invite_form.cleaned_data['email3']
email4 = invite_form.cleaned_data['email4']
email5 = invite_form.cleaned_data['email5']
for i in invite_form.cleaned_data:
invite = Invitation.create(i)
invite.send_invitation(request)
print("The mail was went")
#return reverse('website:ProjectDetails', kwargs = {'pk' : self.pk} )
else:
print("Your form is not valid")
else:
invite_form = InviteForm()
return render(request, 'team_invite.html', {'invite_form': invite_form})
project_detail.html:
{% extends 'base.html' %}
{% block body %}
<div class="container">
<div class="jumbotron">
<h2>Welcome to your Project {{project.project_name}} Detail page</h2>
</div>
<div class="invite-team">
GOGO
</div>
</div>
{% endblock%}
You pass pk variable from urls.py to view method, but your view method did not expect it you have to change your update_profile method signature like so:
update_profile(request, pk)
....
your method has more erros inside like this one:
form = UpdateProfile(request.POST, instance=request.User)
it probaly should be:
User = get_user_model()
user = get_object_or_404(User, pk=pk) #find user instance by id in db
form = UpdateProfile(request.POST, instance=user)
I'm a Python/Django noob. So any help will be appreciated.
Trying to use the django-fluent-contents
models.py
from django.core.urlresolvers import reverse
from django.db import models
from fluent_contents.models.fields import PlaceholderField, PlaceholderRelation, ContentItemRelation
from fluent_contents.models import ContentItem
class Article(models.Model):
title = models.CharField("Title", max_length=200)
slug = models.SlugField("Slug", unique=True)
content = PlaceholderField("article_content")
placeholder_set = PlaceholderRelation()
contentitem_set = ContentItemRelation()
class Meta:
verbose_name = "Article"
verbose_name_plural = "Articles"
def __unicode__(self):
return self.title
def get_absolute_url(self):
return reverse('article-details', kwargs={'slug': self.slug})
admin.py
from django.contrib import admin
from article.models import Article
from fluent_contents.admin import PlaceholderFieldAdmin
class ArticleAdmin(PlaceholderFieldAdmin):
prepopulated_fields = {'slug': ('title',)}
fieldsets = (
(None, {
'fields': ('title', 'slug', ),
}),
("Contents", {
'fields': ('content',),
'classes': ('plugin-holder',),
})
)
admin.site.register(Article, ArticleAdmin)
I'm using South for migration.
db.create_table(u'article_article', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('title', self.gf('django.db.models.fields.CharField')(max_length=200)),
('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=50)),
))
db.send_create_signal(u'article', ['Article'])
It looks like, no column is being created for the 'content' field.
So when I try to add a new 'Article' via django admin —
FieldError at /manage/article/article/add/
Unknown field(s) (content) specified for Article. Check fields/fieldsets/exclude attributes of class ArticleAdmin.
If I remove the fieldset from admin.py
class ArticleAdmin(PlaceholderFieldAdmin):
prepopulated_fields = {'slug': ('title',)}
admin.site.register(Article, ArticleAdmin)
The 'content' field is not shown in django admin
In reply to #vdboor.. Here's my installed apps ...
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
# 3rd party apps
'south',
'django_extensions',
'compressor',
'debug_toolbar',
'fluent_contents',
'fluent_contents.plugins.text',
'django_wysiwyg',
# Project specific apps go here
'article',
)
Also I'm using the example app from the repo as a guide... just removed the extra plugin model
FYI I'm using
Django==1.6
MySQL-python==1.2.4
South==0.8.4
Thank you for all the help :-)
It looks like, no column is being created for the 'content' field.
That is correct, the PlaceholderField becomes a reverse-generic-relation.
You can try removing the fieldsets declaration for now, and see what other error you get.
The repository also contains an example application, which you can run, and compare with your app.
Silly question, but is fluent_contents in the INSTALLED_APPS?
Still need to add extra fields. In the example shown, because the content is stored in a separate table - model ContentItem.
from fluent_contents.models.fields import PlaceholderField, PlaceholderRelation, ContentItemRelation
class Article(models.Model):
title = models.CharField("Title", max_length=200)
slug = models.SlugField("Slug", unique=True)
content = PlaceholderField("article_content")
placeholder_set = PlaceholderRelation() # <-- this
contentitem_set = ContentItemRelation() # <-- and this
class Meta:
verbose_name = "Article"
verbose_name_plural = "Articles"