keep getting integrityError when trying to implement django-multiupload - python

I keep getting the following integrityError when trying to implement the django-multiupload app:
null value in column "post_id" violates not-null constraint
DETAIL: Failing row contains (9, post_images/1899987_718836594813781_835045800_n.jpg, , null, null, null).
I am trying to allow my users to upload multiple pictures to a post. I think where I am going wrong is that it may be saving before being able to get the post id. In my views I have changed the form_valid which may also be effecting it.
Ideally I would like to pass the picture_author and project instances to each image as well, but to get started I have put them = null to figure out how to pass just the post instance at the moment.
Im new to coding & django, so any help in trying to solve it would be much appreciated!
models:
class ProjectPost(models.Model):
project = models.ForeignKey(UserProject)
title = models.CharField(max_length=100)
post_overview = models.CharField(max_length=1000)
date_created = models.DateTimeField(auto_now_add=True)
post_views = models.IntegerField(default=0)
post_likes = models.IntegerField(default=0)
post_author = models.ForeignKey(User, null=True)
class PostImages(models.Model):
post_picture = models.FileField(upload_to='post_images', blank=True)
post = models.ForeignKey(ProjectPost)
picture_description = models.CharField(max_length=100)
picture_author = models.ForeignKey(User, null=True)
project = models.ForeignKey(UserProject, null=True)
forms
class ProjectPostForm(forms.ModelForm):
class Meta:
model = ProjectPost
fields = ('title', 'post_overview')
files = MultiFileField(min_num=1, max_num=20, max_file_size=1024*1024*5)
def save(self, commit=True):
instance = super(ProjectPostForm, self).save(commit)
for each in self.cleaned_data['files']:
PostImages.objects.create(post_picture=each, post=instance)
return instance
views
class NewPost(CreateView):
model = ProjectPost
form_class = ProjectPostForm
template_name = 'howdidu/new_post.html'
def form_valid(self, form):
self.object = form.save(commit=False)
project = UserProject.objects.get(slug=self.kwargs["slug"])
self.object.project = project
form.instance.post_author = self.request.user
self.object.save()
return super(NewPost, self).form_valid(form)
def get_success_url(self):
project_username = self.request.user.username
project_slug = self.object.project.slug
return reverse('user_project', kwargs={'username':project_username, 'slug': project_slug})

You have to save the post before you can add images to it. You need to explicitly tell the get_or_create what the post_id is supposed to be before an image will be saved, and I dont think you're doing that in your code.

Related

NOT NULL constraint failed error when attempting to save using createview

This is my first post to SO, so please let me know if I've missed any important details. I am working on updates to a home-grown dJango based ticketing system.
I have two "parent" models (ParentProjects and Projects) that capture details about work we want to track. Both models have a number of columns that store information in the associated tables as well as some FK relations.
The generic class-based detail view is used to view objects in the Project table while the ParentProject table is accessed by a function based view. The function-based view accomplishes the same task of loading parent project object values as the class-based detail view does for the project objects.
The problem I am having is that I cannot add a new entry to the IDTasks model that automatically inserts the Parent Project id. I am able to add a new IDTask from within the admin site (or from the "client" site if I enable the "parent" field within the modelform) by manually selecting the parent I wish to associate the IDTask to. I can also edit and save an existing IDTask from within the Parent Project detail view without any issues. However, when I attempt to add an IDTask using the createview, dJango reports a Not NULL constraint error and the new entry is not saved.
In addition to reviewing and trying many other solutions to this problem, I have disabling the code that automatically adds the logged in user id but am still getting the same null constraint error. What's strange is that I am using the same basic createview structures for adding FK objects to the Projects model and this works perfectly. I'm still getting comfortable with Django's class-based views, so must surely be missing something obvious.
Thank you for your help!
Here are the main views related to my issue:
# Detail view for Parent Projects (function-based)
def ParentProjectDetail(request,parent_id):
parents = ParentProject.objects.get(id=parent_id)
projects = Project.objects.filter(parent_project__pk=parent_id).order_by('project_phase', '-launch_date',)
return render(request, 'otis/parent_detail.html', context={'parents':parents, 'projects':projects })
# Detail View for Projects (class-based)
class ProjectDetailView(generic.DetailView):
model = Project
context_object_name = 'project'
template_name = 'otis/project_detail.html'
# Add parent progress report
class add_idtask_view(SuccessMessageMixin, CreateView):
model = IDTasks
template_name = 'otis/id_report_form.html'
form_class = idTaskForm
success_message = "Report added"
def form_valid(self, form):
idtaskform = form.save(commit=False)
idtaskform.user = self.request.user
self.parents_id = self.kwargs.get('parent_id')
form.instance.ParentProject = get_object_or_404(ParentProject,id=self.parents_id)
return super(add_idtask_view, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('otis:parent_detail', kwargs={'pk': self.parents_id})
Here is the modelform:
class idTaskForm(ModelForm):
class Meta:
model = IDTasks
fields = ('parent_phase','complete','milestones','nextsteps','concerns')
widgets = {
'milestones': Textarea(attrs={'cols': 50, 'rows': 5, 'placeholder': 'Task details...'}),
'nextsteps': Textarea(attrs={'cols': 50, 'rows': 5, 'placeholder': 'Task details...'}),
'concerns': Textarea(attrs={'cols': 50, 'rows': 5, 'placeholder': 'Task details...'}),
}
labels = {
'parent_phase': mark_safe('<span class="required">Phase</span>'),
'complete': mark_safe('<span class="required">Percentage Complete</span>'),
'milestones': ('Milestones'),
'nextsteps': ('Next steps'),
'concerns': ('Concerns'),
}
Here are the two models being accessed:
# Parent Project Model
class ParentProject(models.Model):
class Meta:
verbose_name = "parent project"
verbose_name_plural = "parent projects"
ordering = ['title']
title = models.CharField('Name', max_length=100, null=True, blank=False)
parent_group = models.ForeignKey(ProjectGroups, on_delete=models.CASCADE, blank=True, null=True)
parent_type = models.ForeignKey(ProjectTypes, on_delete=models.CASCADE, null=True, blank=False)
description = models.TextField('description', blank=True)
term_due = models.ForeignKey(Terms, on_delete=models.CASCADE, blank=True, null=True)
term_year_due = models.ForeignKey(Years, on_delete=models.CASCADE, blank=True, null=True)
launch_date = models.DateField('launch date', blank=True, null=True)
parent_phase = models.ForeignKey(SDLCPhases, on_delete=models.CASCADE, null=True, blank=False)
history = HistoricalRecords()
def __str__(self):
return str(self.title)
def get_absolute_url(self):
return reverse('otis:parent_detail', kwargs={'pk': self.pk})
# Reports Model
class IDTasks(models.Model):
class Meta:
verbose_name = "Parent task"
verbose_name_plural = "Parent tasks"
ordering = ['updated_on']
parent = models.ForeignKey(ParentProject, on_delete=models.CASCADE)
parent_phase = models.ForeignKey(SDLCPhases, on_delete=models.CASCADE, null=True, blank=False)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
complete = models.IntegerField('percentage complete', blank=True, null=True, default=0)
milestones = models.TextField('milestones', blank=True)
nextsteps = models.TextField('next steps', blank=True)
concerns = models.TextField('concerns', blank=True)
updated_on = models.DateTimeField(auto_now_add=True, null=True)
history = HistoricalRecords()
def __str__(self):
return str(self.complete)
Here are the url patterns:
# url for the parent project detail view
path('parent_detail/<int:parent_id>/', views.ParentProjectDetail, name='parent_detail'),
# url for the create report accessed within the detail view
path('parent_detail/<int:parent_id>/add_id_report/', views.add_idtask_view.as_view(), name='add_id_report'),
Finally, the template link that invokes the modelform:
link_title
I was able to solve my initial problem by restructuring the view like so:
class add_idtask_view(SuccessMessageMixin, CreateView):
model = IDTasks
template_name = 'otis/id_report_form.html'
form_class = idTaskForm
success_message = "Report added"
def form_valid(self, form):
parents_pid = self.kwargs.get('parent_pid')
self.parent_id = parents_pid
form.instance.user = self.request.user
form.instance.parent_id = parents_pid
return super(add_idtask_view, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('otis:parent_detail', kwargs={'parent_pid': self.parent_id})
The first thing I did was sort out what form fields and database fields I was trying to access. It seems I had confused these and not properly referenced them in my initial view.
Once these were working however, I started getting an error that stated the view was expecting an integer but was getting a string. It seems, for whatever reason, when I used the get_object_or_404 method, the view returns the title of the database object and not the primary key.

Inserting a 'hidden timestamp field' in a modelform?

I have a simple form that allows a user to create a post. The fields are post_title, post_content, post_date. The post_title and post_content will be provided by the user, however the post_date will be autogenerated by the system and will give the current timestamp. Due to this, I will only show both title and content in the form. When I try and submit a request however, this gives me an IntegrityError saying NOT NULL constraint failed: main_app_post.post_date. I am fairly new at Django so I really have no idea what to do. I want it that whenever the user sends a Post request (and if it passes the validations), it will generate the current timestamp to post_date before saving it to the database. Any advices in regards of this? Thanks a lot!
models.py:
class Post(models.Model):
post_title = models.CharField(max_length=100)
post_content = models.TextField(max_length=400)
post_date = models.DateTimeField()
def __str__(self):
return self.post_title
forms.py:
class PostForm(forms.ModelForm):
class Meta:
model = models.Post
fields = ('post_title', 'post_content')
views.py:
if request.method == 'POST':
form = forms.PostForm(request.POST)
if form.is_valid():
post_title = form.cleaned_data['post_title']
post_content = form.cleaned_data['post_content']
post_date = datetime.datetime.now().timestamp()
form.post_date = post_date
form.save(commit=True)
set auto_now_add=True
class Post(models.Model):
post_title = models.CharField(max_length=100)
post_content = models.TextField(max_length=400)
post_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.post_title
Refernce: Django auto_now and auto_now_add
You can specify a default and make it non-editable, but in fact Django already has a solution for this: you can specify auto_now_add=True [Django-doc]:
class Post(models.Model):
post_title = models.CharField(max_length=100)
post_content = models.TextField(max_length=400)
post_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.post_title
This will make the field non-editable, so it will not show up in a form by default, and furthermore it will use as default value the current timestamp. This thus means that the view and form no longer have to worry about this.

In Django, how do you update all objects when creating a new one with generic.CreateView?

I'm trying to create a model with an "order" field that automatically updates whenever a new object is created. (Later, I want to update it when you drag table rows around as well.)
I started following this tutorial, but I'm stuck in trying to use the manager to actually create the object. This is my simplified code:
# models.py
class Entry(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
owner = models.ForeignKey(
get_user_model(),
on_delete=models.SET_NULL,
null=True,
blank=True
)
order = models.IntegerField(default=1)
objects = EntryManager()
class EntryManager(models.Manager):
def create(self, **kwargs):
instance = self.model(**kwargs)
with transaction.atomic():
results = self.filter(
owner = instance.owner
).aggregate(
Max('order')
)
current_order = results['order__max']
if current_order is None:
current_order = 0
value = current_order + 1
instance.order = value
instance.save()
return instance
#views.py
class EntryCreateView(LoginRequiredMixin,generic.CreateView):
model = Entry
fields = ['name']
def form_valid(self, form):
form.instance.owner = self.request.user
return super().form_valid(form)
success_url = reverse_lazy('home')
When I create a new Entry object here, I get something with an empty order field. Do I need to call the manager directly somehow?
Thanks in advance!

Django editing a model instance fails to find the instance

I'm in the process of making a Recipe Book. For some reason, whenever I try to pull up a recipe from the DB to edit it, I keep getting an error where it can't find the recipe I've specified. I'm using slugs, and my logic is that I'm going from a detailView where I've already pulled up the db information, to an updateView. I'm attempting to pass the recipe object I already pulled from the detailView to the updateView, but when I do, it keeps telling me that it can't find the recipe specified.
views.py:
The base views I'm calling here are only providing a default post method for handling a search so that I don't have to put it in for every view I create so I have some code reusability
class RecipeDetailView(BaseDetailView):
model = Recipe
template_name = 'RecipeBook/recipe_detail.html'
context_object_name = 'recipe_view'
queryset = None
slug_field = 'slug'
slug_url_kwarg = 'slug'
def get_context_data(self, *args, **kwargs):
context = super(RecipeDetailView, self).get_context_data()
recipe = self.object
recipe.ingredients = recipe.ingredients_list.split('\n')
context['recipe'] = recipe
return context
class RecipeEditView(BaseUpdateView):
model = Recipe
template_name = 'RecipeBook/edit_recipe.html'
context_object_name = 'recipe_edit'
queryset = None
slug_field = 'slug'
slug_url_kwarg = 'slug'
form_class = RecipeForm
def get_context_data(self, *args, **kwargs):
context = super(RecipeEditView, self).get_context_data()
recipe = self.object
print(recipe.name)
recipe.ingredients = recipe.ingredients_list.split('\n')
recipe.categories_list = ""
categories = Category.objects.filter(recipe=recipe)
for category in categories:
if category != categories[-1]:
recipe.categories_list += (category + ", ")
else:
recipe.categories_list += category
recipe_edit_form = RecipeForm(initial={'name': recipe.name, 'ingredients_list': recipe.ingredients,
'directions': recipe.directions, 'prep_time': recipe.prep_time,
'cook_time': recipe.cook_time, 'servings': recipe.servings,
'source': recipe.source, 'category_input': recipe.categories_list})
context['recipe'] = recipe
context['recipe_edit_form'] = recipe_edit_form
return context
models.py:
class Recipe(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100, default="")
ingredients_list = models.TextField(default="")
servings = models.IntegerField(default=0, null=True, blank=True)
prep_time = models.IntegerField(default=0, null=True, blank=True)
cook_time = models.IntegerField(default=0, null=True, blank=True)
directions = models.TextField(default="")
source = models.CharField(max_length=100, default="", null=True, blank=True)
categories = models.ManyToManyField(Category, blank=True)
slug = models.CharField(max_length=200, default="")
def __str__(self):
return self.name
urls.py
# ex: /Recipes/Grilled_Chicken/
path('Recipes/<slug>/', views.RecipeDetailView.as_view(), name='view_recipe'),
path('Recipes/<path:slug>/', views.RecipeDetailView.as_view(), name='view_recipe'),
# ex: /Recipes/edit/Steak/
path('Recipes/edit/<slug>/', views.RecipeEditView.as_view(), name='edit_recipe'),
path('Recipes/edit/<path:slug>/', views.RecipeEditView.as_view(), name='edit_recipe'),
link in recipe_detail.html:
Edit Recipe
I've been going nuts trying to figure it out. By everything that I have in here, the recipe that I'm pulling up in the detailView should be able to be passed to the editView, but every time I try to open up the edit_recipe page, it keeps telling me that it can't find the recipe specified. The URL that it generates shows the proper slug and link that it should though. I don't know what I'm missing at this point...
Try this way:
Edit Recipe
I ended up having to go back through and change the view to be a DetailView. That was the only way I could get the recipe instance to be pushed through. There's something very specific about using an update view with models that isn't very clear...
Once I changed to a DetailView, the page would populate with the form initialized to the recipe values. I could then make tweaks to make sure everything worked from there.
Thanks for those who responded, it at least got my brain working in a different direction to get this figured out.

How to pre fill fields in a form when using django-bootstrap-modal-forms

I am using django-bootstrap-modal-forms and it works perfectly as in documentation when using fields form my model. Some of the fields are ForeignKeys and they are displayed properly for user to select a value from database table that is referenced by the key, but instead of that I need to put username of the current user.
I tried to change how the CreateView class handles fields, but with no luck. Probably doing something wrong.
models.py
class userSchoolYear(models.Model):
user_in_school = models.ForeignKey(get_user_model(), null=True, on_delete=models.CASCADE)
school = models.ForeignKey(sifMusicSchool, on_delete=models.CASCADE)
school_year = models.ForeignKey(sifSchoolYear, on_delete=models.CASCADE)
school_year_grade = models.CharField(max_length=4, choices=IZBOR_RAZREDA, default=None, null=True)
user_instrument = models.ForeignKey(instType, on_delete=models.CASCADE, default=None, null=True)
user_profesor = models.ForeignKey(profSchool, on_delete=models.CASCADE, default=None, null=True)
views.py
class SchoolYearCreateView(BSModalCreateView):
template_name = 'school_year.html'
form_class = SchoolYearForm
success_message = 'Success!'
success_url = reverse_lazy('school')
def __init__(self, *args, **kwargs):
self.form_class.user_in_school = 'johnny' ### HERE
print(user.username)
super().__init__(*args, **kwargs)
forms.py
class SchoolYearForm(BSModalForm):
class Meta:
model = userSchoolYear
fields = '__all__'
Thanks to the author Uroš Trstenjak I was able to find a solution. I was wrong trying to set field values from views.py, instead it should be done in forms.py. So, basically I had to write a init for the form and alter fields values. Uroš pointed out that at from level I can get current user from self.request.user and it did work.

Categories