Edit multiple Models using one form or view in Django - python

I am using the default User model in Django and a OneToOneField in a Profile model where extra info such as user bio is stored.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
I am able to create basic forms for the two models independently
from django.contrib.auth.models import User
class UserForm(ModelForm):
class Meta:
model = User
fields = ['username', 'email']
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = ['bio']
What is the best method to create a page where a user can edit fields of either model?
So a User can edit Username,Email or Bio on the same page?

You can put the 2 forms in one template and Django will manage filling forms with the right fields (only exception is the same field name in 2 forms)
def view(request):
if request.method == "GET":
context["userform"]=UserForm()
context["profileform"] =ProfileForm()
else:
userform = UserForm(request.POST)
profileform=ProfileForm(request.POST)

Related

How to get value of model method in Django Rest Framework?

So basically I have a django model that has a ManyToManyField of friends and two methods that run on it. Here are my files:
Models.py:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
first_name = models.CharField(max_length=50, blank=True)
last_name = models.CharField(max_length=50, blank=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
friends = models.ManyToManyField(User, blank=True, related_name='friends')
def friends_list(self):
return self.friends.all()
def number_of_friends(self):
return self.friends.all().count()
Serialzers.py:
from rest_framework import serializers
from .models import Profile
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
Views.py:
from rest_framework import viewsets, permissions
from .models import Profile
from .serializers import ProfileSerializer
class ProfileViewSet(viewsets.ModelViewSet):
queryset = Profile.objects.all()
permission_classes = [
permissions.AllowAny
]
serializer_class = ProfileSerializer
The issue is that in the Api, the return values of the method aren't there. The friends_list method for example is supposed to return a list of friends you have and even though this does work in a traditional django project, the Django Rest Framework is not showing any value for this method. How can I fix this and get the return values for both methods to show up in the api?
Since the model serializer picks up only model fields for the serializer fields, you won't automatically get any methods copied over.
You can still send this read only data over the API by explicitly adding the two fields with reference to the model methods
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = [
# need to explicitly define all fields I believe
'friends_list',
'number_of_friends',
]
Now that the two fields (matching the method name are declared, DRF should create SerializerMethodField or ReadOnly field (not sure which one, but they are similar) for each of them.
It works coz it sets the source for those fields to be the same name, and if finds some attribute (in this case the methods) on the model.
If that doesn't work, you can
class ProfileSerializer(serializers.ModelSerializer):
friends_list = serializers.SerializerMethodField()
number_of_friends = serializers.SerializerMethodField()
class Meta:
model = Profile
fields = [
# need to explicitly define all fields I believe
'friends_list',
'number_of_friends',
]
def get_friends_list(self, instance):
return instance.friends_list()
def get_number_of_friends(self, instance):
return instance.number_of_friends()
when you use __all__ it call fields only you have to call fields with methods using list like that
`fileds = ["first_name","last_name","user",
"friends","friends_list","number_of_friends"
]`

django Models getting user info

trying to create a model that has an artist and song and lets you know what user name typed it in.
so far I have in my models.py
from django.contrib.auth.models import User
class Song(models.Model):
uesrname = models.ForeignKey(User,on_delete=models.CASCADE)
artist = models.CharField(max_length=30)
song=models.CharField(max_length=30)
I added a form that works with user input data but the form lets me select one of all exciting Users and input artist, song
forms.py
class NewSong(forms.ModelForm):
class Meta:
model=Song
exclude = ['username']
how can I change it so I will have only my own loged in user in the form?
You can exclude username field as exclude = ['username'] on your forms.py file and set username to request.user on form's save method. For more information: selecting fields

How to set dynamic initial values to django modelform field

I'm kinda new to django, I need to set a dynamic initial value to my modelform field. I have a database field in my model name 'author' it has a foreignkey that connects it to the django user model. I need to automatically set this to the current user anytime a user fills in information into the form.
from what I gathered about this problem, I'd have to define an __init__ function inside the MyHouseEditForm below, I'm new to django and all the examples I've seen a pretty confusing.
forms.py
from django import forms
from django.contrib.auth.models import User
from .models import Myhouses
class MyHouseEditForm(forms.ModelForm):
class Meta:
model = Myhouses
fields = ('author','name_of_accomodation', 'type_of_room', 'house_rent', 'availability', 'location', 'nearest_institution', 'description', 'image')
i need to set the value of 'author' to the current user anytime a user logs in.
models.py
from django.db import models
from django.contrib.auth.models import User
class Myhouses(models.Model):
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='author')
Available = 'A'
Not_Available = 'NA'
Availability = (
(Available, 'Available'),
(Not_Available, 'Not_Available'),
)
name_of_accomodation = models.CharField(max_length=200)
type_of_room = models.CharField(max_length=200)
house_rent = models.IntegerField(null=True)
availability = models.CharField(max_length=2, choices=Availability, default=Available,)
location = models.CharField(max_length=200)
nearest_institution = models.CharField(max_length=200)
description = models.TextField(blank=True)
image = models.ImageField(upload_to='profile_image')
def __str__(self):
return self.name_of_accomodation
views.py
#login_required
def addlisting(request):
if request.method == 'POST':
form = MyHouseEditForm(request.POST, files=request.FILES)
if form.is_valid():
Houses = form.save(commit=False)
Houses.save()
return redirect('addlisting')
else:
form = MyHouseEditForm()
return render(request, 'houses/addlisting.html', {'form':form })
No need to show author field in form. It would automatically populate with logged in user.
request.user gives you logged in user object. So, you may remove 'author' filed from forms field section and do this:
Houses = form.save(commit=False)
Houses.author = request.user
Houses.save()
I did something like this in the serializer.
I defined a custom create method like this:
class MyhousesSerializer(FlexFieldsModelSerializer):
...
def create(self, validated_data):
validated_data['author'] = self.context['request'].user
newhouse = Myhouses.objects.create(**validated_data)
return newhouse
It shouldn't matter if you use a more regular model serializer.

Django rest framework auto-populate filed with user.id/username

I'm trying to link 'owner' field of my model to an AbstractUser. I need it to be done automatically, the only think i'm able to do by myself is to allow user logged in to choice between every existing user with, what's not what i want. I would like to not have a field to manipulate, but a outcome serializer with id or username of User that added the model. I'm trying to find solutions for a few days, I've tried already combine ForeignKey, PrimaryKeys, OneToOneField, HiddenField, get_user, perform_create, but I'm for sure doing something wrong, and i'm almost lost with it. The last thing i tried is to def_perform in views like DRF QuickStart tutorial say, but without results.
I add some code sample to be more understandable:
There is my AbstractUser model:
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserProfile(AbstractUser):
username = models.CharField(max_length=20, unique=True)
...
i added it to AUTH_USER_MODEL = in the settings.
And there is other model which i want to link with User:
from django.db import models
from users.models.user import UserProfile
class MyPhoto(models.Model):
owner = models.ForeignKey(UserProfile, related_name='photos', on_delete=models.CASCADE, null=True)
image = models.ImageField(upload_to='Images')
serializer.py
class MyPhotoSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = MyPhoto
fields = ('pk', 'image', 'owner')
def create(self, validated_data):
photo = MyPhoto.objects.create(
image=validated_data['image']
)
photo.save()
return photo
views.py
class UpdateMyPhotoViewSet(viewsets.ModelViewSet):
queryset = MyPhoto.objects.all()
serializer_class = MyPhotoSerializer
permission_classes = (IsAuthenticated,)
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)
and for now i can't see the owner field results.
Thanks in advance.

UserProfile missing the User object

I'm using a custom sign up form with django-allauth.
settings.py
ACCOUNT_SIGNUP_FORM_CLASS = 'project.userprofile.form.UserSignupForm'
form.py
from django import forms
from models import UserProfile
class UserSignupForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('mobile_number',)
models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
mobile_number = models.CharField(max_length=30, blank=True, null=True)
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
The User and the UserProfile objects are created, however the UserProfile isn't associated with any User object. It's late and I'm probably missing something silly, right?
UPDATE: As Kevin pointed out, the solution was to add the save method in the form.py. This is how it looks now:
from django import forms
from django.contrib.auth.models import User
from models import UserProfile
class UserSignupForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('mobile_number',)
def save(self, user):
profile = UserProfile(user=user)
profile.mobile_number = self.cleaned_data['mobile_number']
profile.save()
The documentation says:
[ACCOUNT_SIGNUP_FORM_CLASS] should implement a ‘save’ method, accepting the newly signed up user as its only parameter.
It looks like you haven't provided such a method, so the user never gets connected to the profile. And I think you're not seeing an error because ModelForm has a save(commit=True) method that happens to match this signature, even though it doesn't do what you want.

Categories

Resources