1

I want to change my Foreign Key to Many To Many field to let the user select multiple categories in a dropdown list.

This is what I already have. After I change Foreign Key to Many To Many I'm getting milion errors, I have to get rid of on_delete=models.CASCADE which is a core of my app. What can I do? Which way should I take? Maybe add another model? I'm so confused, especially when I am a Django newbie. Thank you for your help!

MODELS

class Category(models.Model):
    name = models.CharField(max_length=50, unique=True)

    def __str__(self):
        return f'{self.name}'


class Expense(models.Model):

    class Meta:
        ordering = ('date', '-pk')

    category = models.ForeignKey(Category, null=True,blank=True, on_delete=models.CASCADE)
    name = models.CharField(max_length=50)
    amount = models.DecimalField(max_digits=8,decimal_places=2)
    date = models.DateField(default=datetime.date.today,db_index=True)

    def __str__(self):
        return f'{self.date} {self.name} {self.amount}'

The clue of the application is to let the user create a category e.g "PC". Then add some expenses like "GPU", "CPU" etc... and let the user link it to the "PC" category. And when the user wants to delete certain categories, all the expenses linked to it, gonna be deleted too. And this is the thing I have already did. BUT NOW I want to let the user search the main table of expenses by multiple categories. And here comes my problem, I don't have a clue how to do it and keep the whole application in one piece with all the functionalities.

SCREENSHOTS:

Categories View with just added PC category

Expense Add View

Giorgi Gvimradze
  • 1,714
  • 1
  • 17
  • 34
micss3
  • 13
  • 4
  • Does this answer your question? [Django - Cascade deletion in ManyToManyRelation](https://stackoverflow.com/questions/3937194/django-cascade-deletion-in-manytomanyrelation) – bart cubrich Sep 17 '20 at 19:14
  • I think you problem is similar to [this](https://stackoverflow.com/questions/3937194/django-cascade-deletion-in-manytomanyrelation). It doesn't make sense to have a may-to-many and an "on delete cascade" for the same model field. The idea with many to many is that each expense can have many categories, so if you delete category from an expense it should persist if it has other categories. Can you describe your desired model objective in more detail? – bart cubrich Sep 17 '20 at 19:16
  • Hi bart, thank you for your time. I'll edit my post to bring more clarity! :) Give me a second – micss3 Sep 17 '20 at 19:23

1 Answers1

0

I don't think there is a simple answer to your question, but here are some resources that might help. First, I don't think you should change your models. From the way you described your application, I think a foreign key model with on_delete=CASCADE is good. The basic idea here is that you need to change your list view function so that it performs a query of your database. Also modify your template.html to include a search bar.

https://github.com/csev/dj4e-samples/tree/master/well

https://www.dj4e.com/lessons/dj4e_ads4

Modify Your List View To Allow The Searching

This is an example of a list view that allows you to search for a single term, and returns anything in the database that matches from any field. This isn't what you want to do exactly, but if you can get this working then you can modify the search conditions for your specific application. What is going on in the code below is that instead of return every item in my Ad table in my SQL database, I filter it based on the search. Then, I pass "ad_list" to the template view. Since I already filtered ad_list based on the search, in the template view it will only list the items that match. This is based on the DJ4E course, and you can watch the video there to get an idea of how he implements the search bar better.

from ads.models import Ad 
from django.views import View
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy, reverse
from django.http import HttpResponse


from django.core.files.uploadedfile import InMemoryUploadedFile
from django.contrib.humanize.templatetags.humanize import naturaltime

from ads.utils import dump_queries

from django.db.models import Q
class AdListView(ListView):
    # By convention:
    template_name = "ads/ad_list.html"

    def get(self, request) :
        strval =  request.GET.get("search", False)
        
        if strval :
            # Simple title-only search
            # objects = Ad.objects.filter(title__contains=strval).select_related().order_by('-updated_at')[:10]

            # Multi-field search
            query = Q(title__contains=strval)
            query.add(Q(text__contains=strval), Q.OR)
            objects = Ad.objects.filter(query).select_related().order_by('-updated_at')[:10]
        else :
            # try both versions with > 4 posts and watch the queries that happen
            objects = Ad.objects.all().order_by('-updated_at')[:10]
            # objects = Ad.objects.select_related().all().order_by('-updated_at')[:10]

        # Augment the post_list
        for obj in objects:
            obj.natural_updated = naturaltime(obj.updated_at)

        ctx = {'ad_list' : objects, 'search': strval}
        retval = render(request, self.template_name, ctx)

        dump_queries()
        return retval;

Modify Your Template.html to include a search bar

<form>
  <input type="text" placeholder="Search.." name="search"
  {% if search %} value="{{ search }}" {% endif %}
  >
  <button type="submit"><i class="fa fa-search"></i></button>
<a href="{% url 'ads:all' %}"><i class="fa fa-undo"></i></a>
</form>

PS, I think you can answer your own question better when you figure it out, so help others and post it!

bart cubrich
  • 1,184
  • 1
  • 14
  • 41