How to save custom fields with user data on Django? - django

I've created a "Account" class so i can get more columns on user information:
class Account(AbstractUser):
company_name = models.CharField(max_length=50)
company_department = models.CharField(max_length=50)
company_employees_quantity = models.IntegerField(default=1)
def __str__(self):
return self.username
I've also created a form with this data so i can receive the information on my view.
class AccountCreationForm(UserCreationForm):
company_name = forms.CharField(max_length=50)
company_department = forms.CharField(max_length=50)
company_employees_quantity = forms.IntegerField()
class Meta:
model = Account
fields = ('username', 'email')
The problem is when the client send the data though the form, i receive all the fields but only the "core" user information is inserted on the database.
class SignUpView(CreateView):
form_class = AccountCreationForm
success_url = reverse_lazy('login')
template_name = 'signup.html'
If o add a method form_valid on on view o can save the data field by field like this:
class SignUpView(CreateView):
form_class = AccountCreationForm
success_url = reverse_lazy('login')
template_name = 'signup.html'
def form_valid(self, form):
if form.is_valid():
account = form.save()
account.company_name = form.cleaned_data["company_name"]
account.company_department = form.cleaned_data["company_department"]
account.company_employees_quantity = form.cleaned_data["company_employees_quantity"]
account.save()
return redirect(self.success_url)
return super().form_invalid(form)
But this look weird to me? the view/form/model shouldn't save my "custom" fields automatically along with the "core" user info? How do i do that?

You don't need to define 'company_name', 'company_department' and 'company_employees_quantity' again, you can add them in the fields attribute.
Try the following:
class AccountCreationForm(UserCreationForm):
class Meta:
model = Account
fields = (
'username',
'email',
'company_name',
'company_department',
'company_employees_quantity',
)

Related

How to assign model form field to a current logged in user in Django's class based views

I am trying to save a form with the current logged in user's username, but the error "Cannot assign "'Neshno_Games2'": "League.host" must be a "Manager" instance." occurs
Views.py
class CreateLeaguesView(generic.CreateView):
model = League
template_name = "leagues/create-league.html"
form_class = LeaguesCreationForm
success_url = "/leagues/leagues"
def get_context_data(self, **kwargs):
context = super().get_context_data( **kwargs)
context['leagues'] = League.objects.all()
return context
def form_valid(self, form):
manager = self.request.user.username
League.objects.create(
host = manager,
)
return super(CreateLeaguesView, self).form_valid(form)
Model.py
class League(models.Model):
name = models.CharField(max_length=30)
no_players = models.IntegerField(default=20)
start_date = models.DateField(blank=False, null=False)
end_date = models.DateField(blank=False, null=False)
prize = models.CharField(max_length=300)
host = models.ForeignKey(Manager, on_delete=models.CASCADE)
def __str__(self):
return self.name
forms.py
class LeaguesCreationForm(forms.ModelForm):
class Meta:
model = League
fields = (
"name",
"no_players",
"start_date",
"end_date",
"prize",
)
You can try like this:
class CreateLeaguesView(generic.CreateView):
model = League
def form_valid(self, form):
form.instance.host= self.request.user.manager # accessing one to one data
return super().form_valid(form)
More information can be found here in this documentation: https://docs.djangoproject.com/en/4.0/topics/db/examples/one_to_one/

Setting some fields to be automatically filled in using CreateVeiw in Django

I'm using Django's CreateView in order to fill a form and I want some of the fields to be automatically filled in, looking for ides how I could that. the fields that I want to be filled in automatically are company, recruiter and date
this is what the views file looks like:
class CreateNewJobForm(CreateView):
model = Job
fields = (
'title', 'company', 'recruiter', 'job_type', 'work_from', 'description', 'city', 'address', 'title_keywords',
'date_created')
template_name = 'create_new_job_form.html'
success_url = '/job_created_successfully'
def form_valid(self, form):
form.instance.recruiter = self.get_name()
return super(CreateNewJobForm, self).form_valid(form)
and this is what the models file looks like:
class Recruiter(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
name = models.CharField(max_length=255)
company = models.ForeignKey(Company, on_delete=models.RESTRICT, related_name='recruiters')
email = models.EmailField(max_length=255)
phone_number = models.CharField(max_length=15, blank=True)
Something like that should work just fine :
def form_valid(self, form):
form.instance.user = self.request.user # assuming you want the current login user to be set to the user
return super(CreateNewJobForm, self).form_valid(form)
It is just an example but in short you can access attributes of your model by accessing the instance of your form like that form.instance.yourfield
Automatically assign the value
We can assign a value without showing this in the form. In that case, you remove the company, recruiter, and date_created fields from the fields, and fill these in in the form_valid method:
from django.contrib.auth.mixins import LoginRequiredMixin
class CreateNewJobForm(LoginRequiredMixin, CreateView):
model = Job
fields = ('title', 'job_type', 'work_from', 'description', 'city', 'address', 'title_keywords')
template_name = 'create_new_job_form.html'
success_url = '/job_created_successfully'
def form_valid(self, form):
recruiter = form.instance.recruiter = self.request.user.recruiter
form.instance.company_id = recruiter.company_id
return super().form_valid(form)
for the date_created, you can work with the auto_now_add=True parameter [Django-doc] of the DateTimeField:
class Job(models.Model):
# …
date_created = models.DateTimeField(auto_now_add=True)
Provide an initial value
We can also provide an initial value for the form by overriding the .get_initial() method [Django-doc]:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.timezone import now
class CreateNewJobForm(LoginRequiredMixin, CreateView):
model = Job
fields = ('title', 'job_type', 'work_from', 'description', 'city', 'address', 'title_keywords', 'company', 'recruiter', 'date_created')
template_name = 'create_new_job_form.html'
success_url = '/job_created_successfully'
def get_initial(self):
recruiter = self.request.user.recruiter
return {
'recruiter': recruiter,
'company': recruiter.company,
'date_created': now()
}

Django rest framework: mixing some fields of one model with another and make a serializer

I have the User model and UserProfile model.
User model contains the following:
email
password
first_name
last_name
and
UserProfile model contains
class UserProfile(models.Model):
user = models.OneToOneField(on_delete=models.CASCADE, primary_key=True)
photo = models.ImageField()
location = models.TextField()
phone = models.CharField(max_length=15)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
I want to create an api endpoint where User can edit the following fields and save after login
--> from User
first_name
last_name
--> from Userprofile
photo
location
phone
In normal Django forms. I can use multiple forms and validate them and save. User will see them as one form.
Eg:
if request.POST():
formA = UserForm(instance=request.user)
formB = UserProfileForm(instance=request.user.userprofile_set)
a_valid = formA.is_valid()
b_valid = formB.is_valid()
# we do this since 'and' short circuits and we want to check to whole page for form errors
if a_valid and b_valid:
a = formA.save()
b = formB.save()
In serializers how can I do this
You can do something like this:
UserSerializer
class UserSerializer(serializer.ModelSerializer):
class Meta:
model = User
fields = ['first_name', 'last_name']
def update(instance, validated_data):
instance.first_name = validated_data.get('first_name', instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.save()
return instance
UserProfileSerializer
class UserProfileSerializer(serializer.ModelSerializer):
class Meta:
model = UserProfileSerializer
fields = ['photo', 'location', 'phone']
def update(instance, validated_data):
instance.photo = validated_data.get('photo', instance.photo)
instance.location = validated_data.get('location', instance.location)
instance.phone = validated_data.get('phone', instance.phone)
instance.save()
return instance
Then create a view like this which receives data in this format:
{
user: {first_name: "", last_name: ""},
profile:{photo: "", location: "", phone: ""}
}
The view
class UserDetailUpdateView(APIView):
def post(self, request, username):
user_data = request.data['user']
profile_data = request.data['profile']
user = get_object_or_404(User, username=username)
serialized_user = UserSerializer(user, data=user_data, partial=True)
serialized_profile = UserProfileSerializer(user.userprofile, data=profile_data, partial=True)
if serialized_user.is_valid() and serialized_profile.is_valid():
serialized_user.save()
serialized_profile.save()
return Response('Updated', status=status.HTTP_200_OK)
return Response('Error occurred', status=status.HTTP_400_BAD_REQUEST)

How to get all form field data in Django UpdateView when working with many-to-many model field?

For some reason my UpdateView is not displaying the selected associated. The form is pre-populated with only the first form field data results, username, and none of the other field data. I suspect this is due to the ManytoMany field being queried. I've searched for and tried possible solutions but nothing has worked. Any ideas?
Models.py
class Company(models.Model):
"""All company specific details. For each main manager, a company is created. The main manager can then add company members, creating for them their user accounts."""
members = models.ManyToManyField(
settings.AUTH_USER_MODEL, related_name='teams', through='CompanyMembership'
)
...
class CompanyMembership(models.Model):
"""Acts as a gateway between User model and Company model"""
STAFF = "STAFF", "Staff"
MANAGERS = "MANAGERS", "Managers"
my_company = models.ForeignKey(Company, on_delete=models.CASCADE)
my_identity = models.ForeignKey(User, on_delete=models.CASCADE)
my_role = models.CharField(max_length=100, choices=MemberTypes.choices, default=None)
class User(models.Model):
"""Users can be main managers or staff. Each main manager has their own Company. Many staff can belong to a single Company associated with a main manager."""
username = models.CharField(_("Login Name"), blank=True, null=True, max_length=155, unique=True)
...
Views.py
class TeamMembersView(LoginRequiredMixin, CompanyMembershipCheckMixin, UserPassesTestMixin, ListView):
"""Lists all the company team members for the specific company"""
model = Company
template_name = 'users/dashboard/team.html'
def test_func(self):
user_obj = User.objects.get(id=self.request.user.id)
return user_obj.groups.filter(name='Company_Main_Manager').exists()
def get_context_data(self, **kwargs):
context = super(TeamMembers, self).get_context_data(**kwargs)
user_obj = User.objects.get(id=self.request.user.id)
companymembership_obj = CompanyMembership.objects.get(my_identity=user_obj)
company_obj = Company.objects.get(id=companymembership_obj.my_company.id)
slug = company_obj.slug
context['members'] = ecosystem_obj.members.all()
context['slug'] = slug
return context
class TeamMemberUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
"""Allows the main manager to change or update the specific staff member's user data"""
model = User
template_name = 'users/dashboard/user_create.html'
form_class = TeamMemberCreateForm
def test_func(self):
user_obj = User.objects.get(id=self.request.user.id)
return user_obj.groups.filter(name='Company_Main_Manager').exists()
def get_context_data(self, **kwargs):
data = super(TeamMemberUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
data['member'] = TeamMemberCreateForm(self.request.POST, self.request.FILES)
else:
data['member'] = TeamMemberCreateForm()
return data
def form_valid(self, form):
context = self.get_context_data()
member = context['member']
with transaction.atomic():
form.instance.created_by = self.request.user
self.object = form.save()
if member.is_valid():
member.instance = self.object
member.save()
return super(TeamMemberUpdateView, self).form_valid(form)
Forms.py
class TeamMemberCreateForm(ModelForm):
"""Allows the main manager to create (and update) a user account for additional staff members."""
first_name = forms.CharField(max_length=200, help_text="Enter member's first name", required=True)
last_name = forms.CharField(max_length=200, help_text="Enter member's last name")
email = forms.EmailField(max_length=200, help_text="Member's primary email address")
phone = forms.CharField(max_length=100, help_text="Primary phone number")
position_title = forms.CharField(max_length=200, help_text="Member's job position or title for the company")
class Meta(UserCreationForm.Meta):
model = User
exclude = ['company_name']
#transaction.atomic
def save(self):
member_user = super().save(commit=False)
member_user.firstname = self.cleaned_data.get('first_name')
member_user.lastname = self.cleaned_data.get('last_name')
member_user.email = self.cleaned_data.get('email')
member_user.phone = self.cleaned_data.get('phone')
member_user.is_company_main_manager = False
member_user.is_active = True
member_user.user_type = User.UserTypes.STAFF
member_user.save()
CompanyMembership.objects.create(my_company=ecosystem_create, my_identity=user, my_role=CompanyMembership.MemberTypes.STAFF)
return member_user
As an example, if I am the main manager I can create a user account for my staff member through the above form. For the staff member account I enter username="username_example", first_name="pete", last_name="smith", phone="333-333-3333",... and so on. CreateView works as expected. The user account is created, saved, etc. UpdateView, though, display would show in this example:
username: username_example
first name:
last name:
phone:
...
# The first field of the form is pre-populated but none of the others... why?
You have to super init your form and rewrite the field with the
def init(self, *args, **kwargs):
super(TeamMemberCreateForm, self).__init__(*args, **kwargs)
self.fields[your_field] = forms.ModelMultipleChoiceField(
queryset=Company.objects.all(),
widget=forms.CheckboxSelectMultiple(attrs=
{'dir': 'ltr', 'type': "checkbox",'switch':"bool"}))
By using fields = ['username', 'firstname', 'lastname', 'email', 'phone'] instead of form_class = TeamMemberCreateForm in UpdateView, the problem was solved.

Filter select field in ModelForm by currently logged in user

I'm trying to display a form (ModelForm) with a select field filtered by currently logged in user. The select field in this case contains a list of categories. I want to display only the categories which "belong" to the currently logged in user. The category field is a foreign key to the IngredienceCategory model.
Here is what I've come up with so far but it's giving me an error (unexpected keyword queryset). Any ideas what I'm doing wrong?
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
class Ingredience(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(IngredienceCategory, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredients"
def __unicode__(self):
return self.name
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
# views.py
def home(request):
if request.user.is_authenticated():
username = request.user.username
email = request.user.email
foods = Food.objects.filter(user=request.user).order_by('name')
ingredients = Ingredience.objects.filter(user=request.user).order_by('name')
ingrcat = IngredienceCategory.objects.filter(user=request.user)
if request.method == 'POST':
form = IngredienceForm(request.POST)
if form.is_valid():
# Create an instance of Ingredience without saving to the database
ingredience = form.save(commit=False)
ingredience.user = request.user
ingredience.save()
else:
# How to display form with 'category' select list filtered by current user?
form = IngredienceForm(queryset=IngredienceCategory.objects.filter(user=request.user))
context = {}
for i in ingredients:
context[i.category.name.lower()] = context.get(i.category.name.lower(), []) + [i]
context2 = {'username': username, 'email': email, 'foods': foods, 'ingrcat': ingrcat, 'form': form,}
context = dict(context.items() + context2.items())
else:
context = {}
return render_to_response('home.html', context, context_instance=RequestContext(request))
That's happening because ModelForm does not take a queryset keyword.
You can probably achieve this by setting the queryset on the view:
form = IngredienceForm()
form.fields["category"].queryset =
IngredienceCategory.objects.filter(user=request.user)
See related question here.
Here i have another suggestion to solve the problem. You can pass request object in your form object inside view.
In view.py just pass the request object.
form = IngredienceForm(request)
In your forms.py __init__ function also add request object
from models import IngredienceCategory as IC
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
def __init__(self, request, *args, **kwargs):
super(IngredienceForm, self).__init__(*args, **kwargs)
self.fields['name'].queryset = IC.objects.filter(user=request.user)
This filter always will be applied whenever you initialize your form .