sachimugu
sachimugu

Reputation: 13

Select a valid choice. That choice is not one of the available choices --Django forms

I am trying to make a drop down select form (category) in django.
The form renders well on the webpage but when I try to submit I get the error Select a valid choice. That choice is not one of the available choices. I have done everything possible within my capability to resolve this. if you have any idea on how to go about this please help.

image

model.py

from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse

# Create your models here.
class Category(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('detail', args=[str(self.id)])
        # return reverse('home')

class Post(models.Model):
    title = models.CharField(max_length=100)
    text = models.TextField()
    author = models.ForeignKey(User,  on_delete=models.CASCADE)
    edited = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now_add=True)
    category = models.CharField(max_length=100, default='coding')

    def __str__(self):
        return f'{self.title} by {self.author} {self.pk}'

    def get_absolute_url(self):
        return reverse('detail', args=[str(self.id)])
        # return reverse('home')

form.py

from django import forms
from .models import Post, Category

choices = Category.objects.all().values_list('name','name')
choices_list = []
for item in choices:
    choices_list.append(item)

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ('title', 'category', 'author','text')
    widgets={
            'title': forms.TextInput(attrs={'class':'form-control'}),
            'category': forms.Select(choices=choices_list, attrs={'class':'form-control'}),
            'author': forms.TextInput(attrs={'class':'form-control', 'id':'author'}),
            # 'author': forms.Select(attrs={'class':'form-control'}),
            'text': forms.Textarea(attrs={'class':'form-control','placeholder':choices_list}),
}

class EditForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ('title', 'text')
        widgets={
            'title': forms.TextInput(attrs={'class':'form-control'}),
            'text': forms.Textarea(attrs={'class':'form-control','placeholder':"less than 500 words"}),
            # 'author': forms.Select(attrs={'class':'form-control'})
}

views.py

class createarticleview(CreateView):
    template_name='posts/addpost.html'
    model = Post
    form_class = PostForm
    #fields = '__all__'
    # fields = ('title','text') for certain fields
    def get_context_data(self, *args, **kwargs):
        cat_menu = Category.objects.all()
        context = super(createarticleview, self).get_context_data(*args, **kwargs)
        context['cat_menu'] = cat_menu
        return context  

addpost.html

{%extends 'index.html'%}
{%block content%}
{% if user.is_authenticated %}
    <div class="container">
        <h3>add post...!!!.{{user.username}}</h3>
        <br>
        <div class="mb-3">
        <form method="POST"> {% csrf_token%}
        {{form.as_p}}
        <button type="submit" class="btn btn-info"> post</button>           
        </form>
    </div>
    </div>

    <script>
        
        var name = "{{user.username}}";
    if(document.getElementById("author").value=name)
    document.getElementById('author').readOnly = true;
  

    </script>
{% else%}
        <h3>you are not logged in</h3>
{%endif%}
{%endblock content%}

Upvotes: 0

Views: 5939

Answers (1)

Sunderam Dubey
Sunderam Dubey

Reputation: 8837

Firstly, Always use PascalCase while defining the name of class, like you can give CreateArticleView rather than createarticleview.

you haven't given choices while defining your model that is Post and given models.CharField().

Update your Post model with choices attribute.

Try this:

models.py


from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse

CATEOGRY_TYPES = (
    ('sp', 'sport'),
    ('te', 'technology'),
    ('bu', 'business')
)


class Category(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('detail', args=[str(self.id)])
        # return reverse('home')


class Post(models.Model):
    title = models.CharField(max_length=100)
    text = models.TextField()
    author = models.ForeignKey(User,  on_delete=models.CASCADE)
    edited = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now_add=True)
    category = models.CharField(
        choices=CATEOGRY_TYPES, max_length=2, default='coding')

    def __str__(self):
        return f'{self.title} by {self.author} {self.pk}'

    def get_absolute_url(self):
        return reverse('detail', args=[str(self.id)])
        # return reverse('home')

views.py

from django.shortcuts import render
from .models import Post, Category
from .forms import PostForm
from django.views.generic.edit import CreateView


class CreateArticleView(CreateView):
    template_name = 'posts/addpost.html'
    model = Post
    form_class = PostForm
    success_url = '/success/'


def success(req):
    return render(req, 'posts/success.html')

Rest of things will be remain same.

You can do it without reverse method by direclty making ForeignKey field in your Post model.

you can also do this:

models.py

from django.db import models
from django.contrib.auth.models import User


class Category(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Post(models.Model):
    title = models.CharField(max_length=100)
    text = models.TextField()
    author = models.ForeignKey(User,  on_delete=models.CASCADE)
    edited = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now_add=True)
    category = models.ForeignKey(
        Category, on_delete=models.CASCADE, default='coding')

    def __str__(self):
        return f'{self.title} by {self.author} {self.pk}'

views.py

from django.shortcuts import render
from .models import Post, Category
from .forms import PostForm
from django.views.generic.edit import CreateView


class CreateArticleView(CreateView):
    template_name = 'posts/addpost.html'
    model = Post
    form_class = PostForm
    success_url = '/success/'


def success(req):
    return render(req, 'posts/success.html')

Your forms.py can be remain same.

Remember: choices while defining models will be given more preference than ForeignKey.

Upvotes: 1

Related Questions