Home > OS >  Why do I need to run the second loop to get the sigle value in django?
Why do I need to run the second loop to get the sigle value in django?

Time:11-18

The project was to create a filter page where users can filter the model data based on their chosen criteria. The whole thing is working but a specific part is not clear and not making sense.

Here is my model-

class Author(models.Model):
    name=models.CharField(max_length=30)

    def __str__(self):
        return self.name

class Kategory(models.Model):
    name=models.CharField(max_length=20)

    def __str__(self):
        return self.name        


class Data(models.Model):
    title=models.CharField(max_length=120)
    author=models.ForeignKey(Author, on_delete=models.CASCADE)
    categories=models.ManyToManyField(Kategory)
    publish_date=models.DateTimeField()
    views=models.IntegerField(default=0)
    reviewed=models.BooleanField(default=False)

    def __str__(self):
        return self.title

Data is the main model while Author was added as ForeignKey and Kategory as many to many fields. My main issue is around this categories field in Data model which has many to many relationship with Kategory model.

Here is my views.py file-

# This view is for the filter page

def is_valid_queryparam(param):
    return param !='' and param is not None

def filter(request):
    qs=Data.objects.all()
    kategory=Kategory.objects.all()
    title_contains_query=request.GET.get('title_contains')
    id_exact_query=request.GET.get('id_exact')
    title_or_author_query=request.GET.get('title_or_author')
    view_count_min=request.GET.get('view_count_min')
    view_count_max=request.GET.get('view_count_max')
    date_min=request.GET.get('date_min')
    date_max=request.GET.get('date_max')
    category=request.GET.get('category')

    if is_valid_queryparam(title_contains_query):
        qs=qs.filter(title__icontains=title_contains_query)

    if is_valid_queryparam(id_exact_query):
        qs=qs.filter(id=id_exact_query)

    if is_valid_queryparam(title_or_author_query):
        qs=qs.filter(Q(title__icontains=title_or_author_query) | Q(author__name__icontains=title_or_author_query))

    if is_valid_queryparam(view_count_min):
        qs=qs.filter(views__gte=view_count_min)   

    if is_valid_queryparam(view_count_max):
        qs=qs.filter(views__lte=view_count_max)

    if is_valid_queryparam(date_min):
        qs=qs.filter(publish_date__gte=date_min)

    if is_valid_queryparam(date_max):
        qs=qs.filter(publish_date__lte=date_max)

    if is_valid_queryparam(category):
        qs=qs.filter(categories=category)     

    test=Data.objects.only('author')

    context={'queryset':qs, 'kategory':kategory, 'test':test}    

    return render(request, 'myapp/filter.html', context)

As you can see in the views.py that I have 2 variables holding all of Data and Kategory model data. I have worked with this type of things many times before. When displaying the data in a table on django template we just have to run a for loop. This time I just cannot get the variable in the categories field. All other fields work fine. Instead of giving me the value it either prints out None or myapp.kategory.none or something similar. I had to run an extra loop to get the categories value. I just cannot make any sense why do I have to run this extra loop to get the value. If anyone can shed some light on it and explain a bit, or even lead me to some literature that explains it that would be helpful.

Following is the template code to show you the extra loop I am running. Look at the 3rd tag to see the extra loop. I should get the value just by journal.categories. Why do I need to run that loop?-

<table >
    <tr >
        <th>Title</th>
        <th>Author</th>
        <th>Category</th>
        <th>Views</th>
        <th>Date Published</th>
        
        
    </tr>
    
    {% for journal in queryset %}
        
    
        <tr >
            <td>{{ journal.title }}</td>
            <td>{{ journal.author }}</td>
            <td>{% for cat in journal.categories.all %}
              {{ cat }}
            {% endfor %}</td>
            <td>{{ journal.views }}</td>
            <td>{{ journal.publish_date }}</td>
            
            
           
        </tr>    
        {% endfor %}    
</table>

CodePudding user response:

You haven't defined ForeignKey.related_name so you should use default so:

<table >
    <tr>
        <th>Title</th>
        <th>Author</th>
        <th>Category</th>
        <th>Views</th>
        <th>Date Published</th>
        
        
    </tr>
    
    {% for journal in queryset %}
        
    
        <tr>
            <td>{{ journal.title }}</td>
            <td>{{ journal.author }}</td>
            <td>{% for cat in journal.categories %}
              {{ cat }}
            {% endfor %}</td>
            <td>{{ journal.views }}</td>
            <td>{{ journal.publish_date }}</td>
            
            
           
        </tr>    
        {% endfor %}    
</table>

Additionally, I'd recommend you to use f-stings in __str__() method of models so:

class Author(models.Model):
    name=models.CharField(max_length=30)

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

class Kategory(models.Model):
    name=models.CharField(max_length=20)

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


class Data(models.Model):
    title=models.CharField(max_length=120)
    author=models.ForeignKey(Author, on_delete=models.CASCADE)
    categories=models.ManyToManyField(Kategory)
    publish_date=models.DateTimeField()
    views=models.IntegerField(default=0)
    reviewed=models.BooleanField(default=False)

    def __str__(self):
        return f"{self.title}"
  • Related