OneToOne field in a django modelform - django

I have a OneToOne field corresponding to a user (user_id), and in my avatar upload form I get a drop down list of all users to select one, but I want django to fill it with the current logged in user. Also, when submitting the form, for some users I get this message "Profile with this User already exists."
For the first problem I think that inline-formsets are the solution, but I don't know how to apply this to my form. I have no clue about the second problem. I am creating the Profile with a signal every time a new user is created, and I don't know how to just update it. I'm new to django.
Here is the code for my model:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar_img = models.ImageField(upload_to='avatars', blank=True)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
The form:
from django import forms
from profiles.models import Profile
class AvatarForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('user', 'avatar_img', )
And the view:
from django.shortcuts import render, redirect
from profiles.forms import AvatarForm
def model_form_upload(request):
if request.method == 'POST':
form = AvatarForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('index')
else:
form = AvatarForm()
return render(request, 'profiles/avatar_form.html', {
'form': form
})
Thanks.
Update with the solution... I added this to the form:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['user'].widget.attrs['disabled'] = 'true'
self.fields['user'].widget = HiddenInput()

You can use initial argument for the form. When creating form object in your view you can give an initial argument for your form.
In your view.
This
form = AvatarForm()
can be replaced with
form = AvatarForm(initial={'user': request.user})
This reference should be helpful to you.

Related

Django question on assigning the created news/team to the user who created it

please help me with one question, if possible.
I have a profile model that has a OneToOneField to User and there is a team field in the Profile model, there is also a Team model with a name, tag, etc. I would like to ask how to make the user who creates the team immediately be in it, so that the team field of the Profile model is assigned this team automatically, so that he is its creator and captain immediately. Maybe someone can help, explain, throw a banal example for understanding.
The creation was done like this, in a separate application. But I don't understand how to give the browser the created tim.
models.py
from django.db import models
from django.contrib.auth.models import User
from slugify import slugify
from django.urls import reverse
class BaseModel(models.Model):
objects = models.Manager()
class Meta:
abstract = True
class Profile(BaseModel):
user = models.OneToOneField(
User, on_delete=models.CASCADE, null=True, blank=True
)
nickname = models.CharField(max_length=30, unique=True, null=True)
team = models.ForeignKey('Team', on_delete=models.SET_NULL, blank=True, null=True)
def save(self, *args, **kwargs):
super(self.__class__, self).save(*args, **kwargs)
if self._state.adding is True:
Profile.objects.create()
def __str__(self):
return self.nickname
class Meta:
verbose_name = "Автор"
verbose_name_plural = "Авторы"
class Team(BaseModel):
name = models.CharField('Название', max_length=50)
tag = models.CharField('Тег', max_length=16, unique=True)
slug = models.SlugField(unique=True, blank=True, null=True)
def __str__(self):
return f'{self.name} [{self.tag}]'
def get_absolute_url(self):
return reverse("team_detail", kwargs={"slug": self.slug})
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Team, self).save(*args, **kwargs)
class Meta:
verbose_name = "Команда"
verbose_name_plural = "Команды"
forms.py
from django import forms
from django.contrib.auth.models import User
from django.forms import TextInput, Textarea, FileInput, IntegerField
from django.forms import TextInput, Textarea, FileInput, Select
from .models import *
class CreateTeamForm(forms.ModelForm):
class Meta:
model = Team
fields = {
'name', 'tag', 'slug'
}
views.py
from django.conf import settings
from django.contrib.auth import authenticate, login, get_user_model
from django.http import HttpResponseRedirect, Http404, HttpResponse
from django.shortcuts import render, redirect, resolve_url
from django.utils.http import url_has_allowed_host_and_scheme
from django.views.generic.base import View
from django.views.generic import DetailView, ListView
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views.decorators.csrf import csrf_exempt
from .models import *
from .forms import *
# Create your views here.
class CreateTeam(View):
def get(self, request):
form = CreateTeamForm(request.POST)
context = {'form': form}
return render(request, 'team/home.html', context)
def post(self, request):
if request.method == 'POST':
form = CreateTeamForm(request.POST)
if form.is_valid():
form.save()
return redirect('home')
return redirect('home')
I'm just learning django, so it's hard to implement everything at once, and I'll be happy to help.
NEW CODE
forms.py
class JoinTeamForm(forms.ModelForm):
key = forms.CharField(label='key', max_length=20)
class Meta:
model = Team
fields = {'key'}
I tried without key = forms.CharField(label='key', max_length=20), but in html {{ form.key }} didn't work.
views.py
class JoinTeam(LoginRequiredMixin, View):
def get(self, request, pk):
print(f'post:{request.POST}, get:{request.GET}')
form = JoinTeamForm(request.POST or None)
team = Team.objects.get(id=pk)
context = {'form': form,
'team': team
}
return render(request, 'team/team_detail.html', context)
def post(self, request, pk):
print(f'post: {request.POST} team_id: {Team.objects.get(id=pk).key}')
profile = request.user.profile
error_msg = 'Неверный код'
if request.method == 'POST':
form = JoinTeamForm(request.POST)
role = Role.objects.get(id=2)
team = Team.objects.get(id=pk)
if form.is_valid():
key = form.save()
if key == team.key:
profile.team = team
profile.role = role
profile.save()
return redirect(team.get_absolute_url())
else:
return HttpResponse(error_msg)
return redirect(team.get_absolute_url())
Could you edit your view to update the user's profile after the team is created?
class CreateTeam(View):
def get(self, request):
form = CreateTeamForm(request.POST)
context = {'form': form}
return render(request, 'team/home.html', context)
def post(self, request):
profile = request.user.profile
if request.method == 'POST':
form = CreateTeamForm(request.POST)
if form.is_valid():
team = form.save()
profile.team = team
profile.save()
return redirect('home')
return redirect('home')
Please note, the way you have this set up is that each profile can only be on one team. If that's your intent, great, but if not you may want to set up a many to many model here so a user can be associated with multiple teams.

Django 3.1, I'm NOT getting RelatedObjectDoesNotExist error for User and Profile models

as the title reads I'm NOT getting the RelatedObjectDoesNotExist error in Django 3.1(Latest Release)
I'm not using signals. I create a superuser using the (python manage.py createsuperuser) command, which, as expected, does not create a profile.
models.py
'''
class User(AbstractUser):
pass
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
birth_date = models.DateField(null=True, blank=True)
'''
views.py
'''
class RegisterView(View):
def get(self, request):
form = UserSignUpForm()
# print(form)
return render(request, 'users/register.html', {'form': form})
def post(self, request):
form = UserSignUpForm(request.POST)
if form.is_valid():
form.save()
username = request.POST.get('username')
Profile.objects.create(user=User.objects.get(username=username))
return redirect('users:login-page')
return render(request, 'users/register.html', {'form': form})
'''
when I use the createsuperuser command no profile is created, so I expect to get RelatedObjectDoesNotExist if I try to sign in. But I do NOT! why is that? also if I edit the database manually and remove a profile and keep the user, the user still works with no RelatedObjectDoesNotExist error!
is this something that has changed with Django 3.1 !
thank you
I actually found the issue.
in the absence of signals connecting User and Profile models, Django couldn't tell a profile was missing for the signed-in user and no error was raised.
Try to create a new signals.py in the same folder and make the create_profile and save_profile functions in the below, that should work.
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()

Django - How do I populate the user field of a model by default in the admin panel based on the current user logged in?

I am trying to create a question-answer forum in django where only the admins are able to respond to a question asked by all the registered users.
models.py
from django.db import models
from django.contrib.auth.models import User
from datetime import datetime
# Create your models here.
class Question(models.Model):
username=models.ForeignKey(User, on_delete=models.DO_NOTHING)
question=models.CharField(max_length=100)
date=models.DateTimeField(default=datetime.now, blank=True)
def __str__(self):
return self.question
class Comments(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{}-{}'.format(self.question.question, str(self.user.username))
admin.py
from django.contrib import admin
from . models import Question, Comments
# Register your models here.
admin.site.register(Question)
admin.site.register(Comments)
views.py
from django.shortcuts import render, redirect
from . models import Question, Comments
from .forms import CommentForm
# Create your views here.
def addQuestion(request):
if request.method == 'POST':
username = request.user
question = request.POST['question']
question = Question(username=username, question=question)
question.save()
# note=Note(title=title, description=description, username=username)
# note.save()
return redirect('/dashboard')
else:
return render(request, "dashboard/question.html")
def viewQuestion(request, question_id):
viewquestion=Question.objects.get(id=question_id)
comments = Comments.objects.filter(question=viewquestion).order_by('-question_id')
context = {
'viewquestion':viewquestion,
'comments':comments
}
return render (request, 'dashboard/questionview.html', context)
As of now, the admin panel provides a drop down based on which I can select a user, but I need the model to display the authenticated admin user by default in the model before adding a comment rather than an admin manually choosing the username.
This is how it looks like currently.
How do I make the dropdown select the current logged in user by default?
Step 1:-
# Pass request params to your model form
admin.py
class CommentsAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
ModelForm = super(CommentsAdmin, self).get_form(request, obj, **kwargs)
class ModelFormMetaClass(ModelForm):
def __new__(cls, *args, **kwargs):
kwargs['request'] = request
return ModelForm(*args, **kwargs)
return ModelFormMetaClass
fields = (('question'), ('user'), ('content',),)
form = CommentsForm
admin.site.register(Comments, CommentsAdmin)
Step 2:-
# Create your form which you have specified for your admin class of comments model (CommentsAdmin)
form.py
class CommentsForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(CommentsForm, self).__init__(*args, **kwargs)
self.fields['user'].initial = self.request.user
class Meta:
model = Comments
exclude = ()

Django send data to Database: NOT NULL constraint failed: sms_post.author_id

I am new to django, I migrated my models, the database is working fine, i can see the data that I added by the manage.py shell. But I cant add Data from my webApp. When I wrote text on the fields and press the submit button it gave me this error NOT NULL constraint failed: sms_post.author_id
Thanks for helping..
models.py files
from django.db import models
from django.contrib.auth.models import User
THE_GENDER = [
("Monsieur", "Monsieur"),
("Madame", "Madame")
]
class Post(models.Model):
name = models.CharField(max_length=100)
email = models.CharField(max_length=100)
gender = models.CharField(max_length=8, choices=THE_GENDER)
number = models.CharField(max_length=100)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
forms.py files
from django import forms
from .models import Post
from crispy_forms.helper import FormHelper
class post_form(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(post_form, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
class Meta:
model = Post
fields = ["name", "email", "gender", "number"]
views.py files
from django.shortcuts import render
from django.http import HttpResponse
from .forms import post_form
from django.contrib.auth.decorators import login_required
#login_required
def home(request):
form = post_form(request.POST or None)
if form.is_valid():
form.save()
context = {
"form": form
}
return render(request, "sms/home.html", context)
You did not set the author of the instance in your for to a User object. You can do this with:
from django.shortcuts import redirect
#login_required
def home(request):
if request.method == 'POST':
form = post_form(request.POST)
if form.is_valid():
form.instance.author = request.user
form.save()
return redirect('name-of-view')
else:
form = post_form()
context = {
'form': form
}
return render(request, 'sms/home.html', context)
In order to implement the Post/Redirect/Get pattern [wiki], in case of a successful POST request, you should make a redirect, for example to the same view. You thus can here replace 'name-of-view' with the name of a view to redirect to.

How can I apply filter to Django modelform and return form to view in Django?

I am trying to get user details in model form to create a Service object.Instead of returning all users from my accounts app, I wanted to apply custom filter 'is_admin = False' in object filter but it is returning users without applying filter. Help me to achieve this....
forms.py
from django import forms
from .models import Service
from accounts.models import User
class AddServiceForm(forms.ModelForm):
class Meta:
model = Service
fields = ['service','title','manager','serviceMobile','alternateMobile',
'latitude','longitude','city','street','landmark','keywords']
def __init__(self, user, *args, **kwargs):
super(AddServiceForm, self).__init__(*args, **kwargs)
self.fields['manager'].queryset = User.objects.all().filter(is_admin=False)
views.py code
class AddService(LoginRequiredMixin, LogoutIfNotAdminMixin, CreateView):
login_url = reverse_lazy('mlogin')
permission_required = 'is_staff'
def post(self, request, *args, **kwargs):
context={}
context['city'] = City.objects.all()
if request.method == 'POST':
form = AddServiceForm(request.POST)
if form.is_valid:
form.save()
return redirect('servicedata')
else:
form = AddServiceForm()
return render(request, 'aapp/locations/service/uservicedata.html', {'form': form, 'context': context})
Sorry for my confusion before. But after going through some documentation, I came to know that inside __init__ method, the super should be used after self.fields.
So your changes should be like:
def __init__(self, user, *args, **kwargs):
self.fields['manager'].queryset = User.objects.all().filter(is_admin=False)
super(AddServiceForm, self).__init__(*args, **kwargs)
I found the way for this while fetching objects we can filter using limit_choices_to in model fields like this...
manager = models.ForeignKey(User, on_delete=models.PROTECT, limit_choices_to={'is_admin':False})