Fill out Django form with data obtained from URL - python

I want to create a new Fallacy (see models.py) via the form to which I get over the url path('<slug:slug_tree>/new/', views.CreateFallacyView.as_view(), name='create_fallacy'),.
So the user is on the TreeDetailView (which corresponds to a certain Tree), and he can add a new Fallacy to this tree. The user should input title and detail, but the Tree (ForeignKey) should be assigned in the code. It should be assigned to the corresponding Tree from which he was directed to the CreateFallacyView.
The slug of the tree is inside the URL, so I thought I could use that information somehow, but I have no idea how I can proceed with that information.
Any help is appreciated! Or probably there are other more elegant solutions?
Many thanks!
models.py
class Tree(models.Model):
title = models.CharField(max_length=50)
detail = models.TextField()
slug = models.SlugField(allow_unicode=True, unique=True, null=True, blank=True)
class Fallacy(models.Model):
title = models.CharField(max_length=50)
detail = models.TextField()
tree = models.ForeignKey(Tree, related_name='fallacy', on_delete=models.CASCADE)
views.py
class CreateFallacyView(generic.CreateView):
form_class = forms.FallacyForm
forms.py
class FallacyForm(forms.ModelForm):
class Meta:
model = models.Fallacy
fields = ('title', 'detail')
urls.py
app_name = 'argtree'
urlpatterns = [
path('', views.TreeListView.as_view(), name='all'),
path('<slug:slug_tree>/', views.TreeDetailView.as_view(), name='tree_detail'),
path('<slug:slug_tree>/new/', views.CreateFallacyView.as_view(), name='create_fallacy'),

In the CreateFallacyView you can link it to the given Tree in the .form_valid(…) method [Django-doc] with:
from django.shortcuts import get_object_or_404
class CreateFallacyView(generic.CreateView):
form_class = forms.FallacyForm
def form_valid(self, form):
tree = get_object_or_404(Tree, slug=self.kwargs['slug_tree'])
form.instance.tree = tree
return super().form_valid(form)

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.

Update model each time a view is generated in django

I'm trying to update my an instance of Post model each time a view PostDetail is generated. So far I've tried multiple approaches but none of them worked. I know that there is ready solution (django-hitcounter) but I would like to write one myself so I can understand what is happening.
The goal there is to add 1 to post.views each time user accesses PostDetail view.
models.py
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='blog_posts')
updated_on = models.DateTimeField(auto_now=True)
content = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
views = models.IntegerField(default=0)
class Meta:
ordering = ['-created_on']
views.py
class PostDetail(generic.DetailView):
model = Post
template_name = 'blog/post_detail.html'
urls.py
urlpatterns = [
path('<slug:slug>/', views.PostDetail.as_view(), name='post_detail'),
]
Once you've got to the point where Django can return a response (eg: it's found the Post object successfully etc...) - you can increment your view count for the object then and proceed to returning the response, so if you change your view to be:
class PostDetail(generic.DetailView):
model = Post
template_name = 'blog/post_detail.html'
def render_to_response(self, context, **kwargs):
self.object.views += 1
self.object.save()
return super().render_to_response(context, **kwargs)

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.

django foreignkey not working

I am still not satisfied with django foreignkey relationship.
below are two classess .
from django.db import models
from django.utils.text import slugify
from django_countries.fields import CountryField
from django.conf import settings
# Create your models here.
class PhotoGallery(models.Model):
title = models.CharField(max_length=255,unique=True)
picture_choices =
models.CharField(max_length=255,choices=PICTURE_CHOICES,default=NTR)
description = models.TextField(default='')
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,related_name='image_uploader')
date_posted = models.DateTimeField(auto_now=True)
views = models.PositiveIntegerField()
likes = models.PositiveIntegerField()
country = CountryField(blank_label='(select country)')
slug = models.SlugField(allow_unicode=True,unique=True)
def __str__(self):
return self.title
from django.contrib import admin
from gallery.models import PhotoGallery
class PhotoGalleryAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('title',)}
And below is class Imagesmodel that has a foreignkey .Calling the PhotoGallery class from class Imagesmodel is not a problem .MY big problem is doing the reverse
class ImagesModel(models.Model):
gala_obj =
models.ForeignKey(PhotoGallery,
related_name='picture_gallery',on_delete=models.CASCADE)
post_image = models.ImageField(upload_to='gallery_pics',
verbose_name='Image')
def __str__(self):
return self.gala_obj.title
and below is my views.py file
`def detailViewPage(request,slug):
#here i wanted to get one image with the given slug
from `ImagesModel`
a = get_object_or_404(PhotoGallery, slug=slug)
selected_pic = a.picture_gallery.all()
#here i wanted to get all images excluding one
with the given slug
object_2 = ImagesModel.objects.exclude(id=selected_pic.id)
.order_by('-id')[:15]
return render(request,'gallery/photogallery_detail.html',{
'selected_pic':selected_pic,
'object_2':object_2,
})`
below is the error i get
The question is not very clear, but I assume you want to get all pictures associated with an object A. In your B model, give your foreignkey a related name:
class B(models.Model):
a = models.ForeignKey(A,related_name='post_title', related_name='pictures')
image = models.ImageField(upload_to='gallery_pics',
verbose_name='Image')
In your views.py:
a = get_object_or_404(A, slug=slug)
b = a.pictures.all()
I don't know why you named your foreignkey 'title', but the above code will get all the pictures associated with the object A.
The following will work also:
a = get_object_or_404(A, slug=slug)
pictures = a.b_set.all()
If you want to get the slug from an object B, then you can do the following:
b = get_object_or_404(B, id=1)
slug = b.a.slug

Django - Listing many different models in one page

I need to list different models in a single page/url.
#models.py
class Service(models.Model):
author = models.ForeignKey(User, related_name="services")
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, unique=True)
objects = ServiceQuerySet.as_manager()
class Carousel(models.Model):
author = models.ForeignKey(User, related_name="carousels")
title = models.CharField(max_length=255)
content = models.TextField()
objects = CarouselQuerySet.as_manager()
This is my views, this way are listed in different pages, I tried to join the queryset, but got no success.
#views.py
class ServiceListView(generic.ListView):
model = models.Service
queryset = models.Service.objects.published()
class CarouselListView(generic.ListView):
model = models.Carousel
queryset = models.Carousel.objects.published()
This is my urls.py, this listing only those services.
urlpatterns = patterns('',
url(r'^$', views.ServiceListView.as_view(), name="service_list"),
url(r'^$', views.CarouselListView.as_view(), name="carousel_list"),
)
I need the two lists appear on the same page. How can I accomplish this task?
What about passing it through the context?
from .models import Service,Carousel
class ServiceListView(generic.ListView):
model = Service
queryset = Service.objects.published()
def get_context_data(self, **kwargs):
context = super(ServiceListView, self).get_context_data(**kwargs)
context['carousel_list'] = Carousel.objects.published()
return context

Categories