'models.py order of the models gives NameError: name 'Category/Post' is not defined

I'm new to Django so this is probably a dumb question but,

when I put the class Category model above the class Post model I get an

NameError: name 'Post' is not defined error.

but when I try to put class Category model underneath the Post model (as in the code here) I get

categories = models.ManyToManyField(Category)
NameError: name 'Category' is not defined error.

models.py

class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    date_posted = models.DateTimeField(default=timezone.now)
    author = models.ForeignKey(User, on_delete=models.CASCADE)  #if is deleted than delete their posts 
    location = models.CharField(max_length=100, default="")
    tags = TaggableManager()
    likes = models.ManyToManyField(User, related_name='blog_posts')
    categories = models.ManyToManyField(Category)


    def total_likes(self):
        return self.likes.count()


    def __str__(self):
        return self.title


    def get_absolute_url(self):
        return reverse('post-detail', kwargs={'pk': self.pk})



class Category(models.Model):
    post = models.ForeignKey(Post, related_name="categories") 
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.name

admin.py

from django.contrib import admin
from .models import Post, Comment, Category #, Konum
# Register your models here.


admin.site.register(Post)
admin.site.register(Comment)
admin.site.register(Category)

#admin.site.register(Konum)


some of the code


<form method="GET" action=".">

        <div class="form-group col-md-4">
                <label for="category">Category</label>
                <select id="category" class="form-control" name="category">
                  <option selected>Choose...</option>
                  {% for cat in categories %}
                  <option value="{{ cat }}">{{ cat }}</option>
                  {% endfor %}
                </select>
        </div>
        <button type="submit" class="btn btn-primary">Search</button>

    </form>

views.py

def home(request):
    context = {
        "posts": Post.objects.all()
    }
    return render(request, 'blog/home.html', context)


#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------------------------------------------------
def filter(request):
    qs = Post.objects.all()
    categories = Category.objects.all()
    id_exact_query = request.GET.get('id_exact')
    title_or_author_query = request.GET.get('title_or_author')
    category = request.GET.get('category')
    
    if is_valid_queryparam(category) and category != 'Choose...':
      qs = qs.filter(categories__name=category)


    context = {
        'posts': qs,
        'categories': Category.objects.all()
    }
    return render(request, 'blog/home.html', context)

class PostListView(ListView):
    model = Post
    template_name = 'blog/home.html'
    context_object_name = 'posts'
    ordering = ['-date_posted']
    paginate_by = 199



class UserPostListView(ListView):
    model = Post
    template_name = 'blog/user_posts.html'
    context_object_name = 'posts'
    paginate_by = 199

    def get_queryset(self):
        return Post.objects.filter(author = user).order_by('-date_posted')

urls.py


from django.urls import path, re_path
from .import views
from .views import PostListView, PostDetailView, PostCreateView, PostUpdateView, PostDeleteView, UserPostListView, TagIndexView, LikeView  #, LocationPostListView

urlpatterns = [
    path('', PostListView.as_view(), name="blog-home"), #has a empty strting bc its already processed blog part in main urls
    path('user/<str:username>', UserPostListView.as_view(), name="user-posts"),
    #--------------------------------------------------------------------------------------------------------------------------------------
    #path('location/<str:loc>', LocationPostListView.as_view(), name="location-posts"),
   #--------------------------------------------------------------------------------------------------------------------------------------

    path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail'),#pk means primary key like post 1 post 2 etc
    path('post/new/', PostCreateView.as_view(), name='post-create'),
    path('post/<int:pk>/update/', PostUpdateView.as_view(), name='post-update'),
    path('post/<int:pk>/delete/', PostDeleteView.as_view(), name='post-delete'),
    path('about/', views.about, name="blog-about"),
#--------------------------------------------------------------------------------------------------------------------------------------
    path('tag/<slug:slug>/', TagIndexView.as_view(), name='tagged'),
    path('like/<int:pk>', LikeView, name='like_post'),
#--------------------------------------------------------------------------------------------------------------------------------------
]


Solution 1:[1]

You can use a string literal to specify the model name of a model that still needs to be defined, so you can use ManyToManyField('Category') or ForeignKey('Post', on_delete=models.CASCADE) for example to refer to models not yet defined:

from django.conf import settings

class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    date_posted = models.DateTimeField(default=timezone.now)
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    location = models.CharField(max_length=100, default="")
    tags = TaggableManager()
    likes = models.ManyToManyField(settings.AUTH_USER_MODELS, related_name='liked_posts')
    categories = models.ManyToManyField('Category')

It however does not seem to make much sense that a Category has a ForeignKey to a post: that would mean that a Category links to exactly one Post record?

You can for example use a ListView with:

class PostListView(ListView):
    model = Post
    template_name = 'blog/home.html'
    context_object_name = 'posts'
    ordering = ['-date_posted']
    paginate_by = 199

    def get_queryset(self):
        qs = super().get_queryset()
        if self.request.GET.get('category'):
            return qs.filter(categories__name=self.request.GET['category'])
        return qs

    def get_context_data(self, *args, **kwargs):
        context = super().get_queryset(*args, **kwargs)
        context['categories'] = Category.objects.all()
        return context

Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1