I'm building a simple blog app using Django. I want to realize the function of adding a new blog using form. Some problems occurs.
Here is my models.py
from django.db import models
from django.utils import timezone
from django.template.defaultfilters import slugify
from django.contrib.auth.models import User
class Blog(models.Model):
title=models.CharField(max_length=60)
content=models.TextField()
author=models.ForeignKey('auth.User',on_delete=models.CASCADE,)
date=models.DateTimeField(default=timezone.now)
slug=models.SlugField(null=True,unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Blog, self).save(*args, **kwargs)
def __str__(self):
return self.title
class UserProfile(models.Model):
user=models.OneToOneField(User)
website=models.URLField(blank=True)
def __str__(self):
return self.user.username
forms.py
from django.template.defaultfilters import slugify
from blog.models import UserProfile
from django.contrib.auth.models import User
class BlogForm(forms.ModelForm):
title=forms.CharField(max_length=60,
help_text="blog title")
content=forms.CharField(help_text="blog content")
author=forms.CharField(help_text="blog author")
date=forms.DateTimeField(help_text="blog date")
class Meta:
model=Blog
fields=('title',)
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model=User
fields = ('username','email','password')
class UserProfileForm(forms.ModelForm):
class Meta:
model=UserProfile
fields=('website',)
the add_blog method in views.py
def add_blog(request):
form=BlogForm()
if request.method =='POST':
form=BlogForm(request.POST)
if form.is_valid():
form.save(commit=True)
return index(request)
else:
print(form.errors)
return render(request, 'add_blog.html',{'form':form})
When I want to add a new blog in my webpage, I can't input the record. It shows me
IntegrityError at /add_blog/
NOT NULL constraint failed: blog_blog.author_id
Could anybody help me fix this problem? Thanks a lot!
In your models, your Blog class requires:
Title
An author, of type auth.User
content
The first step, is to remove the author field from your form:
class BlogForm(forms.ModelForm):
title=forms.CharField(max_length=60,
help_text="blog title")
content=forms.CharField(help_text="blog content")
# author=forms.CharField(help_text="blog author")
date=forms.DateTimeField(help_text="blog date")
class Meta:
model=Blog
fields=('title','content','date')
Next, is to add the logged in user as the author in your view:
from django.shortcuts import redirect
from django.contrib.auth.decorators import login_required
# makes sure this view is called with a valid user
# https://docs.djangoproject.com/en/2.0/topics/auth/default/#the-login-required-decorator
#login_required
def add_blog(request):
form = BlogForm(request.POST or {})
if form.is_valid():
temp = form.save(commit=False)
temp.author = request.user # add the logged in user, as the
# author
temp.save()
return redirect('/')
return render(request, 'add_blog.html',{'form':form})
Another way to view this problem... Perhaps you can Try clearing your migration files , and re-run makemigrations to see if it catches anything off about your models. It may ask you for a default value for some of the fields; and this should ring a bell to assign null=True where appropriate. Personally this is quite a common integrity conflict for me (i'm new to the framework) especially when i've done many unplanned on the fly mods to models on the same db.
Related
So I am building a to do app in Django. I have created databases for the users and todo items. But I have a problem, how can each user have its own data. Like every user should add their own data. It seems like there is no answer out there.
My models.py
class Task(models.Model):
title = models.CharField(max_length=200)
complete = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
My forms.py
class CreateUserForm(UserCreationForm):
class Meta:
model = User
fields = ['username','email','password1','password2']
So how can I connect those both. I have red that I have to use foreign key. But I really don't understand how I can do it
You specify a ForeignKey [Django-doc] in the Task model that refers to the user that constructed it:
# app/models.py
from django.db import models
from django.conf import settings
class Task(models.Model):
title = models.CharField(max_length=200)
complete = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
def __str__(self):
return self.title
You can then make a ModelForm where you exclude the user. For example:
# app/forms.py
from django import forms
from app.models import Task
class TaskForm(forms.ModelForm):
class Meta:
model = Task
exclude = ['user']
Then in the view we can "inject" the user in the instance we create, for example:
# app/views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
from app.forms import TaskForm
#login_required
def create_task(request):
if request.method == 'POST':
form = TaskForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
return redirect('name-of-some-view')
else:
form = TaskForm()
return render(request, 'some_template.html', {'form': form})
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.
I want when add article, current logged user to be added as author, I'm also using ForegnKey to user and want to keep it, but right now throw error:
objects/models.py:
from django.db import models
from users.models import ProfileUser
class Object(models.Model):
author = models.ForeignKey(ProfileUser, on_delete=models.CASCADE)
title = models.CharField(max_length=300)
address = models.CharField(max_length=300)
content = models.TextField()
def __str__(self):
return f"{self.title}"
objects/forms.py:
from django import forms
from .models import Object
class ObjectForm(forms.ModelForm):
class Meta:
model = Object
fields = [
'title',
'address',
'content',
]
objects/views.py:
def add_object(request):
form = ObjectForm(request.POST or None)
if form.is_valid():
obj = form.save(commit=False)
obj.author = request.user
obj.save()
return redirect('home')
context = {
'form': form
}
return render(request, "add_object.html", context)
Also I rewrite default django user model:
users/models.py:
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
# Create your models here.
class ProfileUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_image = models.URLField()
#receiver(post_save, sender=User) # Still don't know how, but next rows create ProfileUser when User is created
def create_user_profile(sender, instance, created, **kwargs):
if created:
ProfileUser.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profileuser.save()
def __str__(self):
return f"{self.user}"
Error:
Cannot assign "<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x04CD3B30>>": "Object.author" must be a "ProfileUser" instance.
The author must be a ProfileUser instance as the error message says, because you declared the author field (of the Object model) as a ProfileUser.
In add_object method, obj.author needs to be a ProfileUser instance, therefore you should get that instance by looking up the user.
def add_object(request):
form = ObjectForm(request.POST or None)
if form.is_valid():
obj = form.save(commit=False)
obj.author = ProfileUser.objects.get(user=request.user)
obj.save()
return redirect('home')
context = {
'form': form
}
return render(request, "add_object.html", context)
If the current user doesn't always exist for the ProfileUser instance, you could use the get_or_create method for the author. Read more about that here
I am developing an employee feedback interface with Django. They shall be able to log in and submit a complaint. That complaint is supposed to be stored in the database with the user who submitted it as an attribute.
I have tried to somehow pass the user to the form so that the form saves the authenticated user's username, but I haven't been able to pass data from a view to a form.
I have been able to integrate a ModelChoiceField() to the ModelForm, but that lets the authenticated user manipulate the username that the complaint is going to be associated with.
models.py:
from django.db import models
from django.contrib.auth.models import User
class Complaint(models.Model):
complaint_text = models.CharField(max_length=1000, default='')
switch_schedule_yes_or_no = models.BooleanField(default=True)
user = models.ForeignKey(User, default=1, on_delete=models.CASCADE)
views.py:
from .forms import ComplaintForm
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
#login_required()
def complaint_view(request):
form = ComplaintForm(request.POST)
if form.is_valid():
form.save()
form = ComplaintForm()
context = {
'form': form,
}
return render(request, 'complaint.html', context)
forms.py:
from django import forms
from .models import Complaint
from django.contrib.auth.models import User
class ComplaintForm(forms.ModelForm):
complaint_text = forms.CharField(max_length=1000)
switch_schedule_yes_or_no = forms.BooleanField()
user = forms.ModelChoiceField(queryset=User.objects.all())
class Meta:
model = Complaint
fields = ['complaint_text', 'switch_schedule_yes_or_no', 'user']
If it is possible to somehow design this is a way that allows the complaint to be associated with the authenticated user, that would be amazing! Be it by passing parameters from a view to a form or by using user-individual URLS. I have been trying to solve this for days now.
Cheers!
You can use request.user to access the authenticated user and associate with you Complaint object. You don't need the user field in the ComplaintForm form.
#login_required()
def complaint_view(request):
form = ComplaintForm(request.POST)
if form.is_valid():
complaint = form.save(commit=False) #don't commit to DB
complaint.user = request.user #access the user
complaint.save() # save and commit to DB
form = ComplaintForm()
context = {
'form': form,
}
return render(request, 'complaint.html', context)
Try the following Class Based View Approach
In your form, you can ommit this line:
user = forms.ModelChoiceField(queryset=User.objects.all())
from django.views import generic
from my_app.forms import ComplaintForm
class ComplaintView(generic.CreateView):
template_name = "complaint.html"
form_class = ComplaintForm
def form_valid(self, form):
form.instance.user = self.request.user # This is where what you want happens
super().form_valid(form)
And to add the login required constraint, you can use the LoginRequiredMixin:
from django.contrib.auth.mixins import LoginRequiredMixin
class ComplaintView(LoginRequiredMixin, generic.CreateView):
pass
I´m having a really hard time with this. I have extended the Django user model. I created a separate app call "userprofile" (i have 2 apps: 'userprofile' and 'Administration') with new models.py:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
profile_image = models.ImageField(upload_to="/perfil/", blank=True, null=True)
User.profile = property(lambda u: UserProfile.objects.get_or_create(user = u)[0])
the urls.py:
urlpatterns = patterns('',
url(r'^perfil/$', 'apps.userprofile.views.user_profile', name= 'perfil'),
)
and a views.py:
# Create your views here.
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.core.context_processors import csrf
from forms import UserProfileForm
from django.contrib.auth.decorators import login_required
#login_required
def user_profile(request):
if request.method == 'POST':
form = UserProfileForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect('../index')
else:
user = request.user
profile = user.profile
form = UserProfileForm(instance = profile)
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('profile.html', args)
and of course a forms.py:
from django import forms
from models import UserProfile
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Layout
from crispy_forms.bootstrap import (FormActions, )
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
helper = FormHelper()
helper.form_method = 'POST'
helper.layout = Layout(
'profile_image',
FormActions(Submit('Editar', 'Editar', css_class= 'btn-primary'))
)
def save(self, commit=True):
fact = super(UserProfileForm, self).save(commit=False)
if commit:
fact.save()
return fact
So, what i´m trying to do is to let the user upload an image an let it use it as a profile image. I set the:
AUTH_PROFILE_MODULE = 'apps.userprofile.UserProfile' (the app is inside a folder call 'apps' that´s why the first 'apps' before userprofile)
in the settings.py, and i added the urls of 'userprofile' to the main project. Now I have the template where i can upload an image, the problem is that the image is never saved in the database so I can´t call a function to display the image in a template, let´s say the User Profile page.
Does anyone looking at the code knows what I am doing wrong?
According to the Django 1.7 docs, ModelForm classes must explicitly specify which fields should be updated when the save() method is called. Try adding fields = __all__ to your UserProfileForm class meta.
Relevant section of ModelForm documentation
Notice the first line of the Note in that link:
Any fields not included in a form by the above logic will not be set
by the form’s save() method.
I am still learning Django myself but that's what I would try first. :)
In the Django Administrative Interface I'd like to Automatically Insert a logged in users username along with a blog post when the publish it, currently I have it displaying every user in a drop down to select from but obviously this is not great so I'd like it to automatically input this.
Here is my code:
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Post(models.Model):
title = models.CharField(max_length=200)
body = models.TextField()
published_date = models.DateTimeField('date published')
author = models.ForeignKey(User, db_column="published_who")
def __unicode__(self):
return self.title
admin.py
from blog.models import Post
from django.contrib import admin
class PostAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
admin.site.register(Post, PostAdmin)
Many Thanks!
As I understand issue you need to exclude author from admin form:
class PostAdmin(admin.ModelAdmin):
exclude = ['author']
What you should use is in the Django docs: https://docs.djangoproject.com/en/1.3/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_foreignkey
You can overwrite the default behaviour of a ForeignKeyField in the admin with this.
Something along the lines of:
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name == "author":
kwargs["initial"] = request.user
return super(PostAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
*This is untested
EDIT:
I didn't know whether you wanted to entirely disable the dropdown. With this method you wont. Instead you will have a default value of request.user but still be able to select another user.
If you want to make it a drop down with only one selection (weird behaviour :P) you could add:
kwargs["queryset"] = Post.objects.filter(author=request.user)