Django - Auto populate created_by field outside django admin using form - django

The users are asked to create a new instance outside the django admin by filling a form created by the modelformset_factory. The problem is that I dont know how to pass request.user to the form so the created_by field is not valid when the form is saved.
models.py:
from django.db import models
from django.contrib.auth.models import User
class ezApp(models.Model):
name = models.SlugField(max_length=50, unique=True )
date_created = models.DateTimeField('date created', auto_now_add=True)
date_updated = models.DateTimeField('date updated', auto_now=True)
created_by = models.ForeignKey(User)
in_use = models.BooleanField()
views.py
from django.shortcuts import render_to_response
from ezmapping.models import *
from django.forms.models import modelformset_factory
def setName(request):
ezAppFormSet = modelformset_factory(ezApp, extra=1, fields=('name'))
formset = ezAppFormSet(queryset=ezApp.objects.none())
if request.method == 'POST':
formset = ezAppFormSet(request.POST, request.FILES)
if formset.is_valid():
formset.save()
return render_to_response("project/manage_new.html", {'formset': formset, 'title': "New"}, context_instance=RequestContext(request))

You can set the created_by field yourself before saving the instance.
Do something like this:
if formset.is_valid():
instances = formset.save(commit=False)
for instance in instances:
instance.created_by = request.user
instance.save()
The documentation about this feature is here.

Related

IntegrityError at * NOT NULL constraint failed: main_post.owner_id

I want to create a PostModel(just like instagram) and while the form is created to connect the user to the model with One-to-one/foreign key relationship, anyway I'm getting a problem while trying to upload an image and the db doesn't updates.
I've tried this solution
...
# models.py
from django.contrib.auth.models import User
from django.conf import settings
class Post(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
description = models.CharField(max_length=255, blank=True)
image = models.ImageField(upload_to='images')
uploaded_at = models.DateTimeField(auto_now_add=True)
...
# forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('description', 'image', )
def save(self, commit=True):
if commit:
Post.save()
return Post
...
# views.py
def account(request):
post = PostForm(request.POST, request.FILES)
if request.method == "POST":
if post.is_valid():
post.save(commit=False)
post.owner = request.user
post.save(commit=True)
messages.success(request, f"you had successfully updated your profile image")
return redirect("main:account")
else:
for msg in form.error_messages:
messages.error(request, f"{msg}: {form.error_messages[msg]}")
return render(request = request,
template_name = "main/account.html",
context={'PostForm':post})
post = PostForm()
return render(request = request,
template_name = "main/account.html",
context={'PostForm':post})
You should not override the def save() method, this is fine as it is now, so:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('description', 'image', )
# no save
as for the view, you need to add the owner to the object, but here you are adding it to the form, and that thus has no effect (on the object):
from django.contrib.auth.decorators import login_required
#login_required
def account(request):
post = PostForm(request.POST, request.FILES)
if request.method == 'POST':
if post.is_valid():
post.instance.owner = request.user
post.save()
messages.success(request, f'you had successfully updated your profile image')
return redirect('main:account')
# …
I would also advise to rename post to post_form, since this is a form, not a post object.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].

Django ModelForm some fields aren't saved

I'm using a ModelForm in Django but some fields are not saved to the database...
models.py file
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.forms import ModelForm
# Create your models here.
class Bill(models.Model):
image_name = models.CharField(max_length=150)
upload_date = models.DateTimeField(default=timezone.now)
image = models.ImageField()
description = models.TextField(blank=True)
result = models.CharField(max_length=1000)
uploaded_by = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
def __str__(self):
return str(self.result + self.description)
forms.py file
from django import forms
from django.db import models
from django.forms import ModelForm
from .models import Bill
class BillForm(ModelForm):
class Meta:
model = Bill
fields = ['image', 'description']
exclude = ['result', 'image_name', 'upload_date', 'uploaded_by']
views.py file
def upload(request):
if request.method == 'POST':
form = BillForm(request.POST, request.FILES)
if form.is_valid():
form.image_name = request.FILES['image']
form.upload_date = datetime.now()
form.uploaded_by = request.user
form.result = "something"
form.save()
return redirect('cism-home')
else:
form = BillForm()
return render(request, 'auth/upload.html', {'form': form})
So the image and description fields are saved but other fields are not. Any ideas why is that?
Your form is excluding some fields, so you can't "access" those fields using:
form.upload_date (for example), because they don't exists.
What you can do is:
if form.is_valid():
bill = form.save(commit=False)
bill.image_name = request.FILES['image']
bill.upload_date = datetime.now()
bill.uploaded_by = request.user
bill.result = "something"
bill.save()
If you want a quick description about what "commit=False" do, you can check:
Django ModelForm: What is save(commit=False) used for?

Django edit user profile after success registration

I try to configure the project in such a way that the user goes through the primary registration, then logged in and from the personal cabinet could add additional information about himself. There are no problems with the initial registration, but when you try to change and supplement the information, there are no changes.
My UserProfile model:
models.py
from customuser.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50, blank=True)
surname = models.CharField(max_length=50, blank=True, default = None)
avatar = models.ImageField(upload_to = 'images/profile/%Y/%m/%d/', blank=True, null=True)
position = models.ForeignKey('Position',blank=True, default=None)
role = models.ForeignKey('Role', blank=True, default=None)
company = models.ForeignKey('Company',blank=True, default=None)
status = models.ForeignKey('Status', blank=True, default=None)
#receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
forms.py
from django import forms
from django.forms import ModelForm
from client.models import Profile
class UserProfileForm(ModelForm):
class Meta:
model = Profile
exclude = ['user']
views.py
from customuser.models import User
from client.models import Profile
from .forms import UserProfileForm
def edit_user(request, pk):
user = User.objects.get(pk=pk)
form = UserProfileForm(instance=user)
if request.user.is_authenticated() and request.user.id == user.id:
if request.method == "POST":
form = UserProfileForm(request.POST, request.FILES, instance=user)
if form.is_valid():
update = form.save(commit=False)
update.user = user
update.save()
return HttpResponse('Confirm')
else:
form = UserProfileForm(instance=user)
return render(request, 'client/edit.html', {'form': form})
UserProfileForm is form for Profile model, so instance passed to the form should be profile, not user. You shoud do something like this:
def edit_user(request, pk):
user = User.objects.get(pk=pk)
profile = user.profile
form = UserProfileForm(instance=profile)
if request.user.is_authenticated() and request.user.id == user.id:
if request.method == "POST":
form = UserProfileForm(request.POST, request.FILES, instance=profile)

Add model relation to ModelForm before saving

models.py
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=32)
class Article(models.Model):
author = models.ForeignKey(Author, on_delete=models.SET_NULL, null=True)
content = models.TextField()
forms.py
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ['content']
In my web application, the author logs in and writes an article. So clearly, when the author is presented with an ArticleForm, he/she does not need to fill in the author field in the ArticleForm because the application already knows who the author is through the use of session variables.
This is the way I tried to add the author:
views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from .models import Article
from .forms import ArticleForm
#login_required
def new_article(request):
author_name = request.session['author_name']
author = Author.objects.get(name=author_name)
if request.method == 'POST':
form = ArticleForm(request.POST)
if form.is_valid():
form.save(commit=False)
form.author = author # I suspect the mistake is here
# I also tried form.author = author.id
form.save()
return redirect('success')
else:
form = ArticleForm()
return render(request, 'writings/new_article.html', {'form': form})
When I look at the database table, the author_id column is always NULL. What is wrong with my approach? How do I add a model relation before saving a ModelForm?
Capture the object returned from form.save(commit=False) and modify that rather than the form. EG:
if form.is_valid():
article = form.save(commit=False)
article.author = author
article.save()
return redirect('success')

UserProfile.location" must be a "Location" instance

I am still experiencing some problems with understanding forms and relationships between model-forms.
I am getting an error:
UserProfile.location" must be a "Location" instance.
I've set location in models to blank=True, null=True and required=False in forms.py.
And I dont really know at this point what to do with that.
How I can fix that problem?
from django.db import models
from django.contrib.auth.models import User
class Location(models.Model):
location = models.CharField(max_length=32)
#county = models.CharField(max_length=32)
#province = models.CharField(max_length=32)
class UserProfile(models.Model):
user = models.OneToOneField(User, unique=True, primary_key=True)
location = models.ForeignKey("Location", null=True, blank=True)
website = models.URLField("Site", null=True, blank=True)
accepted_rules = models.BooleanField(default=False)
accepted_rules_date = models.DateTimeField(auto_now_add=True)
#points_count = models.IntegerField(default=0, null=True, blank=True)
#posts_count = models.IntegerField(default=0, null=True, blank=True)
#comments_count = models.IntegerField(default=0, null=True, blank=True)
Forms:
from django import forms
from django.forms import Form
from django.forms.models import ModelForm
from accounts.models import UserProfile, Location
from django.contrib.auth.models import User
class UserCreationForm(forms.Form):
username = forms.CharField(max_length=32)
password = forms.CharField(widget=forms.PasswordInput())
email = forms.EmailField()
#password_repeat = forms.CharField(widget=forms.PasswordInput(render_value=False))
def clean_username(self):
try:
# iexact = case-insensitive match / important for validation
User.objects.get(username__iexact=self.cleaned_data['username'])
print "User does already exist"
except User.DoesNotExist:
return self.cleaned_data['username']
else:
raise forms.ValidationError("User already exists")
def clean_email(self):
if User.objects.filter(email__iexact=self.cleaned_data['email']):
print u'Adres email jest już używany.'
raise forms.ValidationError('Adres email jest już używany.')
else:
return self.cleaned_data['email']
def save(self):
user = User.objects.create(username = self.cleaned_data['username'], email = self.cleaned_data['email'],)
user.set_password(self.cleaned_data['password'])
return user
class UserProfileForm(ModelForm):
website = forms.URLField(label="website", required=False)
location = forms.ChoiceField(required=False)
class Meta:
model = UserProfile
include = ['website', 'location']
exclude = ('user', 'type', 'accepted_rules')
Views
contrib.auth.models import User
from django.template.response import TemplateResponse
from django.views.decorators.csrf import csrf_protect
from django.core.context_processors import csrf
from django.forms.models import inlineformset_factory
from django.http import HttpResponseRedirect
from accounts.forms import UserCreationForm, UserProfileForm
def index(request):
return TemplateResponse(request, "base.html")
#csrf_protect
def register(request):
form = UserCreationForm()
user_profile = UserProfileForm()
if request.method == "POST":
form = UserCreationForm(prefix='user', data=request.POST or None)
user_profile = UserProfileForm(prefix='profile', data= request.POST or None)
if form.is_valid() and user_profile.is_valid():
user = form.save()
profile = user_profile.save(commit=False)
profile.user = user
profile.save()
return HttpResponseRedirect("/")
return TemplateResponse(request, 'accounts/register.html', {
'form':form,
'user_profile':user_profile ,
}
)
The problem is here.
class UserProfileForm(ModelForm):
website = forms.URLField(label="website", required=False)
location = forms.ChoiceField(required=False)
class Meta:
model = UserProfile
include = ['website', 'location']
exclude = ('user', 'type', 'accepted_rules')
ModelForm will generate needed fields for your form. You don't need to define them manually. So you should use something like this.
class UserProfileForm(ModelForm):
class Meta:
model = UserProfile
include = ['website', 'location']
exclude = ('user', 'type', 'accepted_rules')
Another thing. There is no include option, I think you wanted to use fields. But you don't have to use both fields and exclude, usually you need to use one them. In your case exclude is enough. Final result:
class UserProfileForm(ModelForm):
class Meta:
model = UserProfile
exclude = ('user', 'type', 'accepted_rules')