Models not rendering in Django templates - python

I'm trying to pass an abstract model to an inclusion tag like via takes_context=True. The abstract model contains choices for a model field. I want to pass the abstract model instead of hardcoding the choices in the template to stay DRY. While debugging I realized that the template isn't receiving the model as expected.
# urls.py
...
urlpatterns = [
path('', IndexView.as_view(), name='index'),
]
# views.py
...
class IndexView(TemplateView):
"""Home Page"""
template_name = 'index.html'
def get_context_data(self, **kwargs):
kwargs['model'] = MyModel
return super(IndexView, self).get_context_data(**kwargs)
...
# index.html
{{model}}
The above renders nothing in the browser. When I change the variable to a string, the context renders as expected.
# views.py
...
class IndexView(BaseSearchBarMixin, TemplateView):
"""Home Page"""
template_name = 'index.html'
def get_context_data(self, **kwargs):
kwargs['model'] = 'testing 123'
return super(IndexView, self).get_context_data(**kwargs)
...
# index.html
{{model}} # fixed typo
# browser
testing 123
I have a feeling I'm doing something stupid but don't know what
EDIT:
Per the accepted answer, passing classes to templates isn't possible. Since the class I wanted to pass is an abstract model, there are cases where MyModel.objects.first() could return an empty queryset. I ended up making a custom ContextMixin that added the choices to my class based views.
# myapp.models.users.py
class MyModel(models.Model):
"""shared fields and functions for MyModel models"""
class Meta:
abstract = True
DOWN_VOTE = 'DOWN'
UP_VOTE = 'UP'
VOTE_CHOICES = [
(DOWN_VOTE, 'Down vote'),
(UP_VOTE, 'Up vote'),
]
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
vote = models.CharField(choices=VOTE_CHOICES, default=UP_VOTE, max_length=255)
# views.py
...
class MyModelChoicesContextMixin(ContextMixin):
"""add choices from abstract model to context"""
def get_context_data(self, **kwargs):
"""add choices from abstract model to context"""
context = super(MyModelChoicesContextMixin, self).get_context_data(**kwargs)
context['user_DOWN_vote'] = MyModel.DOWN_VOTE
context['user_UP_vote'] = MyModel.UP_VOTE
return context
class IndexView(MyModelChoicesContextMixin, BaseSearchBarMixin, TemplateView):
"""Home Page"""
template_name = 'index.html'

You are passing in a class, not an instance of the class or a queryset, try:
kwargs['model'] = MyModel.objects.first()
(for example - to get the first).
You can't use the class in the template.

In kwargs you are passing keyword 'model' but in template you are using 'Mymodel' keyword that's why empty template is showing.
2nd thing display model fields like model.field_name.

Related

Use URL to set model as true or false

I have a todo APP and I would like to have a link where the user can click and set the "todo" as complete without deleting it from my database.
I use CBV but cannot figure out how do it :
I tried
views.py :
class TodoDeleteView(LoginRequiredMixin, DeleteView):
model = Todo
success_url = '/'
template_name = 'dashboard/dashboard_confirm_delete.html'
def completeTodo(request, todo_id):
todo = Todo.objects.get(pk=todo_id)
todo.complete = True
todo.save()
But it delete it from my DB and it does not set it to true.
My models.py
class Todo(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE,verbose_name="Nom de l'utilisateur")
text = models.CharField(max_length=150, verbose_name="Nom de la Todo")
complete = models.BooleanField(default=False)
You define a DeleteView, and deleting the object, is just part of the delete control flow. In order to change the behavior, we can override the delete function, like:
class TodoDeleteView(LoginRequiredMixin, DeleteView):
model = Todo
pk_url_kwarg = 'todo_id'
success_url = '/'
template_name = 'dashboard/dashboard_confirm_delete.html'
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
success_url = self.get_success_url()
self.object.complete = True
self.object.save()
return HttpResponseRedirect(success_url)
The pk_url_kwarg is necessary to use this to obtain the primary key to filter and retrieve the correct object with get_object().
The reason why we might want to use a DeleteView, is because people can make a DELETE request on that endpoint.

Django - selectively query models using pk_url_kwarg

Consider the these two models and view:
models.py
class BHA_List(models.Model):
well = models.ForeignKey(WellInfo, 'CASCADE', related_name='bha_list')
bha_number = models.CharField(max_length=100)
class BHA_overall(models.Model):
bha_number = models.ForeignKey(BHA_List, 'CASCADE', related_name='bha_overall')
drill_str_name = models.CharField(max_length=111)
depth_in = models.CharField(max_length=111)
views.py
class BHA_UpdateView(UpdateView):
model = BHA_overall
pk_url_kwarg = 'pk_alt'
form_class = BHA_overall_Form
To my understanding, pk_url_kwarg = 'pk_alt' will query and return instances of model = BHA_overall.
Let's say that I use a different CBV other than UpdateView, and want to implement two models. So something like this:
model = (BHA_overall, BHA_List). Is there any way that I force my pk_url_kwarg = 'pk_alt' to query and return instances only in BHA_List, but force my get_object() return objects in BHA_overall?? What CBV should I use?
you can use just 'View' and define methods post and get.
about like this:
class SomeView(View):
model = BHA_List
template_name = 'some.html'
def get(request, **kwargs):
overall = BHA_overall.objects.all()
return render(request,
self.template_name,
locals())
def get(request, **kwargs):
return render(request, self.template_name, {})

How to filter for current object on FormView

I'm having this urls.py
...
url(r'^storageitem/(?P<pk>[\w]+)/merge/$', login_required(
StorageItemMergeView.as_view()), name='storage_item_merge'),
...
with this view.py
...
class StorageItemMergeView(FormView):
form_class = MergeStorageItemsForm
success_url = reverse_lazy('storage_item_list')
template_name = 'pmgmt/storageitem/merge.html'
...
As the URL might look like localhost:8000/storageitem/155/merge/ I'd like to exclude 155 from the form. I tried to define a custom queryset
queryset = StorageItem.objects.exclude(pk=kwargs["pk"])
but kwargs is seems not be present at this very point.
...
queryset = StorageItem.objects.exclude(pk=kwargs["pk"])
NameError: name 'kwargs' is not defined
What is the correct way to create a FormView based on a model with all items beside the current one?
You can add get_form_kwargs method to your view:
def get_form_kwargs(self):
kwargs = super(ItemCreate, self).get_form_kwargs()
kwargs.update({
'exclude_pk' : self.kwargs['pk']
})
return kwargs
And filter queryset in your form field like this:
class MergeStorageItemsForm(Form):
# Form fields
def __init__(self, *args, **kwargs):
exclude_pk = kwargs.pop('exclude_pk')
super(FooForm, self).__init__(*args, **kwargs)
if exclude_id:
self.fields['some_field'].queryset = self.fields['some_field'].queryset.exclude(pk=exclude_pk)

Pass context data from generic.DetailView

How can i pass the context data which is coming from a forms.py to my views class which is using generic detailView, i need to pass forms.py to my product detail page.
Here is the code for my view class
class ProductView(generic.DetailView):
model = Product
cart_product_form = CartAddProductForm()
context = {'cart_product_form': cart_product_form}
template_name = 'shopping/product.html'
query_pk_and_slug = True
Please let me know if this is incorrect
Override get_context_data, and add the form to the context before returning it.
class ProductView(generic.DetailView):
model = Product
template_name = 'shopping/product.html'
query_pk_and_slug = True
def get_context_data(self, **kwargs):
context = super(ProductView, self).get_context_data(**kwargs)
cart_product_form = CartAddProductForm()
context['cart_product_form'] = cart_product_form
return context

How can I return regular response in override get method, Django

I try to use class based views in Django. And I have such problem: I define a base class for a blog (BlogBaseView) and two other classes, that inherit it.
And in the second class(BlogIndexView) I want to make the search by get request, so I have override get method. It works, but if I don't make get request, it returns HttpResponse, however I want to return usual context (which BlogIndexView retunes without override get method).
What can I do?
class BlogBaseView(View):
def get_context_data(self, **kwargs):
context = super(BlogBaseView, self).get_context_data(**kwargs)
blog_categories = []
categories = BlogCategory.objects.all()
for category in categories:
blog_categories.append(tuple([category, category.get_number_of_category_items]))
context['name_page'] = 'blog'
context['tags'] = Tag.objects.all()
context['blog_categories'] = blog_categories
return context
class BlogIndexView(BlogBaseView, ListView):
queryset = Post.objects.all().order_by('-date_create')
template_name = 'index_blog.html'
context_object_name = 'posts'
def get(self, request):
if request.GET.get('tag'):
context = {
'posts' : Post.objects.filter(tags__name__in=[request.GET.get('tag')])
}
return render(request, self.template_name, context)
return HttpResponse('result')
class BlogFullPostView(BlogBaseView, DetailView):
model = Post
template_name = 'full_post.html'
pk_url_kwarg = 'post_id'
context_object_name = 'post'
Thanks!
ListView class also has a get_context_data method, so you should override that instead of get method. Using super you'll get access to BlogBaseView.get_context_data and then you can extended the result.
Here's how:
class BlogIndexView(BlogBaseView, ListView):
queryset = Post.objects.all().order_by('-date_create')
template_name = 'index_blog.html'
context_object_name = 'posts'
def get_context_data(self, **kwargs):
# use the (super) force Luke
context = super(BlogIndexView, self).get_context_data(**kwargs)
if self.request.GET.get('tag'):
context['posts'] = Post.objects.filter(tags__name__in=[self.request.GET.get('tag')])
return context
If you are overriding ListView then it's not a good idea to override the get method, as you will lose a lot of the ListView functionality.
In this case, it would be a better idea to override get_queryset, and do the search there.
def get_queryset(self):
queryset = super(BlogIndexView, self). get_queryset()
if request.GET.get('tag'):
queryset = queryset.filter(tags__name=request.GET['tag'])
return queryset

Categories