Django edit user profile with Class Based Views - python

I am trying to develop a profile edit page in Django 3.1. I am new to this so if something is funky just say so. I copied some of this from Django edit user profile
Views.py
from myapp.forms import UserForm, UserProfileInfoForm
from django.views.generic import (View, TemplateView, UpdateView)
from myapp.models import UserProfileInfo
class UpdateProfile(UpdateView):
model = UserProfileInfoForm
fields = ['first_name', 'last_name', 'email', 'phone', 'title', 'password']
template_name = 'profile.html'
slug_field = 'username'
slug_url_kwarg = 'slug'
models.py
class UserProfileInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone = PhoneField(blank=True, help_text='Contact phone number')
title = models.CharField(max_length=255)
system_no = models.CharField(max_length=9)
def __str__(self):
return self.user.username
forms.py
#...
from myapp.models import UserProfileInfo
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta():
model = User
fields = ('username', 'email', 'password', 'first_name', 'last_name')
class UserProfileInfoForm(forms.ModelForm):
class Meta():
model = UserProfileInfo
fields = ('phone', 'title', 'system_no')
myapp/urls.py
from django.urls import path
from . import views
app_name = 'myapp'
urlpatterns = [
path('', views.index,name='index'),
path('about/',views.AboutView.as_view(),name='about'),
path('register/', views.register,name='register'),
path('profile/', views.UpdateProfile.as_view(), name='profile'),
]
I have a link that only show after login in:
base.html (href only)
<a class="nav-link active" href="{% url 'myapp:profile' %}">Profile</a>
Can you tell me what to fix? After clicking on the above link I get an error message in the browser
AttributeError at /profile/
type object 'UserProfileInfoForm' has no attribute '_default_manager'

well you have done very good till here. Note these :
1.if you are using UpdateView class based view the model field must be the name of the model not the form.(UserProfileInfo)
2.if you are using class based view it is better for you to have a success url in class to redirect after successful attempt
3.if you are using UpdateView no need to have form in forms.py for that model. all you need is a form tag in your template update view will handle it for you and save the changes automatically.
if you want to use form (if you want to save sth in instances that class with not automatically do it ) , better to use FormView and remember here you have to save the form by yourself
link to FormView
If you want to use django for long time I strongly recommend to check this link and start reading the documents https://docs.djangoproject.com/en/3.1/ref/class-based-views/generic-editing/#django.views.generic.edit.FormView

Related

Overwrite 'Personal info' field in the admin/Users?

During my first Django app i've managed to hit this 'road-block' where i'm trying to extend User model from my .forms file where forms.py looks like this:
#forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(required=True)
phone = forms.CharField(max_length=50, required=False)
class Meta:
model = User
fields = ['username', 'email', 'phone', 'password1', 'password2']
class UserLoginForm(AuthenticationForm):
class Meta:
model = User
fields = ['username', 'password']
In my .models file i'm only having a 'Profile' model which is being registered within admin.py:
#admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import *
from .forms import UserRegisterForm
admin.site.unregister(User)
class CustomUserAdmin(UserAdmin, UserRegisterForm):
fieldsets = UserAdmin.fieldsets + (
(('Personal info'), {'fields': ('phone',)}),
)
admin.site.register(User, CustomUserAdmin)
admin.site.register(Profile)
...and i'm getting this back(bare in mind i've tried also passing just my form class which resulted in allot more errors):
#error
FieldError at /admin/auth/user/1/change/
Unknown field(s) (phone) specified for User. Check fields/fieldsets/exclude attributes of class CustomUserAdmin.
Request Method: GET
Request URL: http://127.0.0.1:8000/admin/auth/user/1/change/
Django Version: 3.0.4
So the end goal would be to have another field available in my Users/'Personal info'(that's been extended within forms.py) and also would be nice to get that field when creating a new user from within the admin page. Any help/idea would be greatly appreciated, thanks.

How to display function based view endpoint in django rest frameworks

I am having a hard time to understand how to display function based views URLS with Django REST FRAMEWORK.
I have the setup below of my project but for some reason I am unable to display the endpoint while MovieListViewSet works.
PROJECT.URLS
from users import views
router = routers.DefaultRouter()
router.register(r'movies', MovieListViewSet)
urlpatterns = [
path('', include(router.urls)),
path('admin/', admin.site.urls),
path('profile/', views.ProfileList, name='ProfileList')
]
users.model
User = settings.AUTH_USER_MODEL
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500)
location = models.CharField(max_length=50)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
def __str__(self):
return self.user.username
serializers
from rest_framework import serializers
from users.models import Profile
class ProfileSerializer(serializers.ModelSerializer):
user = serializers.StringRelatedField(read_only=True)
class Meta:
model = Profile
fields = (
'id',
'user',
#'bio',
#'location',
'image',
)
I have comment bio and location because when they are uncommented, I receive this message.
Got AttributeError when attempting to get a value for field `bio` on serializer `ProfileSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `QuerySet` instance.
Original exception text was: 'QuerySet' object has no attribute 'bio'.
users.views (app)
#api_view(["GET"])
def ProfileList(APIView):
profile = Profile.objects.all()
serializer = ProfileSerializer(profile)
return Response(serializer.data)
I am unable to see ProfileList view as endpoint
Can someone point me to what I am doing wrong to display this endpoint to django rest framework.
You should specify many=True while serialization.
serializer = ProfileSerializer(profile, many=True)
You are mixing up functions and class based view definition here:
#api_view(["GET"])
def ProfileList(APIView):
profile = Profile.objects.all()
serializer = ProfileSerializer(profile)
return Response(serializer.data)
Either you define a class based view like this:
class ProfileView(APIView):
profile = Profile.objects.all()
serializer = ProfileSerializer
def list(request):
return Response(self.serializer_class(self.get_queryset(), many=True).data)
Or a function base view like:
#api_view(["GET])
def profile_list(request):
return Response(ProfileSerializer(Profile.objects.all(), many=True).data)

The User ForiegnKey is now showing up as a field in django rest framework viewset

I am building a backend for a web app using django rest framework. I have a profile model that has a user forieingkey referencing a django user. Everything is loading correctly except for one issue which is that the User field is not showing up in the django rest framework backend urls so that I can assign a user to the profile object i want to create... does anyone know why this is happening...
models.py:
class Profile(models.Model):
user = models.ForeignKey(
User, on_delete=models.CASCADE
)
synapse = models.CharField(max_length=25, null=True)
bio = models.TextField(null=True)
profile_pic = models.ImageField(
upload_to='./profile_pics/',
max_length=150
)
facebook = models.URLField(max_length=150)
twitter = models.URLField(max_length=150)
updated = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user.username + ' profile'
viewset:
from users.models import Profile
from users.api.serializers.ProfileSerializer import ProfileSerializer
from rest_framework import viewsets
class ProfileViewSet(viewsets.ModelViewSet):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
lookup_field = 'user__username'
url:
from users.api.views.ProfileView import ProfileViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'', ProfileViewSet, base_name='profile')
urlpatterns = router.urls
serializer:
from rest_framework import serializers
from users.models import Profile
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = (
'id',
'user',
'synapse',
'bio',
'profile_pic',
'facebook',
'twitter'
)
depth=2
That happens when you set a depth bigger that 0, foreign key fields become not editable (if you send a POST with that field containing some value, DRF viewset would ignore it, and if the field is required, it will raise an exception).
One solution for that is to override to_representation() method of the serializer and set the depth and restore it to zero:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = (
'id',
'user',
'synapse',
'bio',
'profile_pic',
'facebook',
'twitter'
)
def to_representation(self, instance):
self.Meta.depth = 2
representation = super().to_representation(instance)
self.Meta.depth = 0
return representation

How to display the valueof a newly created custom model field in Django?

I have created a custom model in models.py
class UserProfile(models.Model):
#required by the auth model
user = models.OneToOneField(User, on_delete=models.CASCADE)
NativeLanguage = models.CharField(max_length=100)
LearningLanguage = models.CharField(max_length=100)
And also created an inline in admin.py, like this:
class ProfileInline(admin.StackedInline):
model = UserProfile
can_delete = False
verbose_name_plural = 'UserProfile'
# Define a new User admin
class UserAdmin(BaseUserAdmin):
inlines = (ProfileInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
I can give these two new fields values in the admin app, but when I try to call them in my html, I dont know how to reference them.
<p align="center">Native Language: {{????}}</p>
It must be something simple, but I cant seem to get it.
First of all, model field names should be lowercase underscore separated:
class UserProfile(models.Model):
...
user = models.OneToOneField(User, related_name='profile')
native_language = models.CharField(max_length=100)
Later anywhere in the template or a view:
{{ user.profile.native_language }}
First of all html page renders from view.py
you have to send value of Native language from view to html page
in views native language is retrieved from models ..
In views.py ::
from .models import (model class name)
data = model class name.objects.all().values('NativeLanguage')
return render(request, 'html page', {"data":data})
Something like this will render data from database to html page
then in html page
<p align="center">Native Language: {{data}}</p>

django1.6 ModelForm Make fields readonly

I have a django model
models.py
class Item(models.Model):
author = models.ForeignKey(User)
name = models.CharField('Brief summary of job', max_length=200)
created = models.DateTimeField('Created', auto_now=True,auto_now_add=True)
description = models.TextField('Description of job')
I would like to update the description field in a modelform using UpdateView. I would like to see the other fields (author,name etc) but want editing and POST disabled
forms.py
from django.forms import ModelForm
from todo.models import Item
class EditForm(ModelForm):
class Meta:
model = Item
fields = ['author', 'job_for', 'name', 'description']
urls.py
urlpatterns = patterns('',
# eg /todo/
url(r'^$', views.IndexView.as_view(), name='index'),
#eg /todo/5
url(r'^(?P<pk>\d+)/$', views.UpdateItem.as_view(), name='update_item'),
)
views.py
class UpdateItem(UpdateView):
model = Item
form_class = EditForm
template_name = 'todo/detail.html'
# Revert to a named url
success_url = reverse_lazy('index')
I have looked at the solution suggesting form.fields['field'].widget.attrs['readonly'] = True but I am unsure where to put this or how to implement
Field.disabled
New in Django 1.9.
The disabled boolean argument, when set to True, disables a form field using the disabled HTML attribute so that it won’t be editable by users. Even if a user tampers with the field’s value submitted to the server, it will be ignored in favor of the value from the form’s initial data.
def MyForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self):
self.fields['field'].disabled = True

Categories