Valuerror in admin form - django

Following is my model and form:
models.py
class Employee(models.Model):
id = models.PositiveIntegerField(primary_key=True, verbose_name='Employee Code')
name = models.CharField(max_length=200, verbose_name='Employee Name')
def get_names(self):
return Employee.objects.values_list('name', 'name')
class JobQueue(models.Model):
emp_name = models.ForeignKey(Employee, on_delete=models.CASCADE)
product_code = models.ManyToManyField(Product)
forms.py
class JobQueueForm(forms.ModelForm):
emp = Employee()
prod = Product()
emp_name = forms.ChoiceField(choices = emp.get_names)
product_code = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=prod.get_products)
def save(self, commit=True):
return super(JobQueueForm, self).save(commit = commit)
class Meta:
model = JobQueue
fields = ('emp_name', 'product_code')
When I try to add new employee from JobQueue form, I get the following error:
ValueError at /admin/timesheet/jobqueue/add/
Cannot assign "'some_name'": "JobQueue.emp_name" must be a "Employee" instance.
Any idea what I am doing wrong here?

You have a name clash, basically, which is a red herring for you when debugging this.
You have emp_name in your form which is a ChoiceField (string) but when you save the form to the database the backing model JobQueue is expecting emp_name to be an instance of an employee.
In your case you'd need to override the save method in your form, which you've shown intent on doing already, and fetch the employee there overriding emp_name and saving it.
def save(self, commit=True):
self.emp_name = Employee.objects.get(name=self.cleaned_data['emp_name'])
return super(JobQueueForm, self).save(commit=commit)

Related

Cannot assign "id": "Product.category" must be a "CategoryProduct" instance

i'm working on a django project and i got this error (Cannot assign "'11'": "Product.category" must be a "CategoryProduct" instance.) anyone here can help me please.
Model:
class Product(models.Model):
name = models.CharField("Nombre", max_length=150)
category = models.ForeignKey(CategoryProduct, on_delete=models.SET_NULL, null=True, related_name='category')
def __str__(self):
return self.name
View:
class ProductCreateView(CreateView):
model = Product
form_class = ProductForm
success_url = '/adminpanel/products/'
def post(self, request, *args, **kwargs):
form = self.get_form()
category = CategoryProduct.objects.get(id=request.POST['category'])
if form.is_valid():
product = form.save(commit=False)
product.category = category
product.save()
Form:
class ProductForm(forms.ModelForm):
name = forms.CharField(max_length=150, label="Nombre")
category = forms.ChoiceField(choices=[(obj.id, obj.name) for obj in CategoryProduct.objects.all()], label="Categoría")
class Meta:
model = Product
fields = ['name', 'category']
You can let Django's ModelForm do its work, this will create a ModelChoiceField [Django-doc], which is where the system gets stuck: it tries to assign the primary key to category, but that should be a ProductCategory object, so you can let Django handle this with:
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ['name', 'category']
If you want to specify a different label, you can use the verbose_name=… [Django-doc] from the model field, or specify this in the labels options [Django-doc] of the Meta of the ProductForm. So you can specify Categoria with:
class Product(models.Model):
name = models.CharField('Nombre', max_length=150)
category = models.ForeignKey(
CategoryProduct,
on_delete=models.SET_NULL,
null=True,
related_name='products',
verbose_name='Categoria'
)
def __str__(self):
return self.name
then the CreateView can just use its boilerplate logic:
class ProductCreateView(CreateView):
model = Product
form_class = ProductForm
success_url = '/adminpanel/products/'
Note: The related_name=… parameter [Django-doc]
is the name of the relation in reverse, so from the Category model to the Product
model in this case. Therefore it (often) makes not much sense to name it the
same as the forward relation. You thus might want to consider renaming the category relation to products.

How to filter data dynamically by supply values from form using django

I want to filter Blog Post objects or records based on the Post Category and a User that uploaded the Post record, it gives me an error when I try to do filter, this is the error.
ValueError at /dashboard/filter-post/
The QuerySet value for an exact lookup must be limited to one result using slicing.
Here is my models.py
class Category(models.Model):
cat_name = models.CharField(max_length=100, verbose_name='Category Name')
cat_desc = models.TextField(blank=True, null=True)
def __str__(self):
return self.cat_name
class Meta():
verbose_name_plural='Category'
class Post(models.Model):
pst_title = models.CharField(max_length=150)
pst_image = models.ImageField(blank=True, null=True, upload_to='uploads/')
user = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ManyToManyField(Category)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.pst_title
#property
def img_url(self):
if self.pst_image:
return self.pst_image.url
on forms.py
class FilterForm(forms.ModelForm):
user = forms.ModelChoiceField(
queryset=User.objects.all(),
widget=forms.Select(attrs={'class': 'form-control'}))
category = forms.ModelMultipleChoiceField(
queryset=Category.objects.all(),
widget=forms.SelectMultiple(attrs={'class': 'form-control js-example-disabled-results'}))
catch_bot = forms.CharField(required=False,
widget=forms.HiddenInput, validators=[validators.MaxLengthValidator(0)])
class Meta():
fields = ['user', 'category' ]
model = Post
on views.py
def filter_post(request):
post = FilterForm(request.GET)
queryset = Post.objects.all()
if post.is_valid():
user=post.cleaned_data.get('user')
category=post.cleaned_data.get('category')
if user and category:
queryset = queryset.filter(user__username=user, category__cat_name=category)
return render(request, 'backend/filter-post.html', {'query':queryset, 'post':post})
I am having challenges properly filtering this in my views any help?
Try this:
instead of this:
queryset = queryset.filter(user__username=user, category__cat_name=category)
use this:
queryset = queryset.filter(user=user, category=category)
Also don't name your model fields after the model name, just use name instead of pst_name or cat_name, you will see that when you will try access these values there will be no confusion.
UPDATE
Ok, maybe try to rewrite your view like this:
def filter_post(request):
posts = Post.objects.all()
form = FilterForm(request.GET) # its best practice to call your form instance `form` in the view so that the next line has better readability
if form.is_valid():
user=post.cleaned_data['user']
category=post.cleaned_data['category']
if user:
posts = posts.filter(user=user)
if category:
posts = posts.filter(category=category)
return render(request, 'backend/filter-post.html', {'posts':posts})

Show model values as multi select options in admin

I am trying to store use the values from two different models to show as choices in form and store them in new model.
Here is what I have now.
models.py
class Employee(models.Model):
name = models.CharField(max_length=200)
class Product(models.Model):
code = models.CharField(primary_key=True, max_length=5)
class JobQueue(models.Model):
emp_name = models.CharField(max_length=200)
product_code = models.CharField(max_length=5)
forms.py
class JobQueueForm(forms.ModelForm):
emp_choices = Employee._meta.get_field('name').choices
product_code_choices = Product._meta.get_field('code').choices
emp_name = forms.ChoiceField(choices = emp_choices)
product_code =forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=product_code_choices)
def save(self, commit=True):
return super(JobQueueForm, self).save(commit = commit)
class Meta:
model = JobQueue
fields = ('emp_name', 'product_code')
admin.py
class JobQueueAdmin(admin.ModelAdmin):
form = JobQueueForm
fieldsets = (
(None,{
'fields': ('emp_name', 'product_code'),}),
)
def save_model(self, request, obj, form, change):
super(JobQueueAdmin, self).save_model(request, obj, form, change)
admin.site.register(models.Employee, AuthorAdmin)
admin.site.register(models.Product, ProductAdmin)
admin.site.register(models.JobQueue, JobQueueAdmin)
I do have values stored in Employee and Product models, but I dont see them as options in JobQueue model in Admin portal.
Am I missing anything here?
emp_choices = Employee._meta.get_field('name').choices
This line doesn't make sense. It tries to get the choices for the Employee.name field, but you did not specify any choices for the name field in your model.
class Employee(models.Model):
name = models.CharField(max_length=200)
If you want generate a list of choices from all the existing employees in the database, then you can define a method that does this:
def get_names():
return Employee.objects.values_list('name', 'name')
Then use this method in your form:
class JobQueueForm(forms.ModelForm):
emp_name = forms.ChoiceField(choices=get_names)
...

Multi select input in admin site

I am trying to create a form in Admin site that uses two fields from two different tables (Employee, Product) as input (single select & multi-select) and make it available for admin user selection and write this to another table (JobQueue).
Following is my code.
models.py
class Employee(models.Model):
id = models.IntegerField(primary_key=True, verbose_name='Employee Code')
name = models.CharField(max_length=200, verbose_name='Employee Name')
def __str__(self):
return self.name
class Product(models.Model):
STATUS = (('New', 'New'), ('Go', 'Go'), ('Hold', 'Hold'), ('Stop', 'Stop'))
code = models.IntegerField(primary_key=True, max_length=3, verbose_name='Product Code')
name = models.CharField(max_length=100, verbose_name='Product Name')
def __str__(self):
return self.name
class JobQueue(models.Model):
emp_name = models.CharField(max_length=200, default='1001')
product_code = models.CharField(max_length=200, default='100')
admin.py:
class JobQueueAdmin(admin.ModelAdmin):
form = JobQueueForm
fieldsets = (
(None,{'fields': ('emp_name', 'product_code'),}),)
def save_model(self, request, obj, form, change):
super(JobQueueAdmin, self).save_model(request, obj, form, change)
forms.py:
class JobQueueForm(forms.ModelForm):
# Single select drop down
emp_name = forms.ModelChoiceField(queryset=Employee.objects.all(), widget=forms.ChoiceField())
# Multiselect checkbox
product_code = forms.MultiValueField(queryset=Product.objects.all(), widget=forms.CheckboxSelectMultiple(), required=False)
def save(self, commit=True):
return super(JobQueueForm, self).save(commit = commit)
class Meta:
model = JobQueue
fields = ('emp_name', 'product_code')
When I start the web-server, I get the following error:
AttributeError: 'ModelChoiceField' object has no attribute 'to_field_name'
Could someone please help me how do I let the user to pick the values from JobQueueForm and save the same in JobQueue table ?

Django formview returns object already exists error with a modelform

I'm trying to create a FormView that receives a string but it gives me a "objects already exists" error when I complete the field I give. What I'm trying to do is to create a view that checks if a certain "product" (model) exists, if that product really exists, redirect to another view based on the product "pk" to create another model.
Basically the course of action is like this:
Check if product exists.
if exists redirect to create order (model) view, else no nothing.
Fill the create order form, if valid, create the order and assign the product fk relation to order.
Here's my code
views.py
class BuyOrderCheckProduct(generic.FormView):
template_name = 'buy_order/buy_order_check_product.html'
form_class = forms.CheckProductForm
def form_valid(self, form):
try:
product = Product.objects.get(codename=form.cleaned_data['codename'])
except Product.DoesNotExist:
product = None
if product:
# Never enters here because correct existing codename gives form_invalid, don't know why
return super(BuyOrderCheckProduct, self).form_valid()
else:
# It only enters when I input a non-existent codename for product
return super(BuyOrderCheckProduct, self).form_invalid()
def form_invalid(self, form):
# I don't know why it enters here!
return super(BuyOrderCheckProduct, self).form_invalid()
def get_success_url(self, **kwargs):
# TODO: How to pass product pk as kwargs?
return reverse_lazy('order_create', self.kwargs['pk'])
class BuyOrderCreate(generic.CreateView):
template_name = 'buy_order/buy_order_create.html'
form_class = forms.BuyOrderCreateForm
success_url = reverse_lazy('buy_order_list')
# TODO: Need to create a custom form_valid to add product fk to order.
forms.py
class CheckProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ['codename']
class BuyOrderCreateForm(forms.ModelForm):
class Meta:
model = BuyOrder
models.py
"""
ORDER
"""
class Order(models.Model):
class Meta:
verbose_name = u'orden'
verbose_name_plural = u'ordenes'
abstract = True
unit_price = models.IntegerField(u"precio unitario", )
quantity = models.IntegerField(u"cantidad", default=1)
discount = models.IntegerField(u"descuento")
def __unicode__(self):
return self.code
class BuyOrder(Order):
class Meta:
verbose_name = u'orden de compra'
verbose_name_plural = u'ordenes de compra'
product = models.ForeignKey(Product, related_name="buy_orders", editable = False)
bill = models.ForeignKey(BuyBill, related_name="orders", null=True, editable = False)
"""
PRODUCT
"""
class Product(models.Model):
class Meta:
verbose_name = u'producto'
verbose_name_plural = u'productos'
category = models.ForeignKey(Category, verbose_name=u'categoría', related_name='products')
codename = models.CharField(u"código", max_length=100, unique=True)
name = models.CharField(u"nombre", max_length=100)
description = models.TextField(u"descripción", max_length=140, blank=True)
sale_price = models.IntegerField(u"precio de venta", default=0)
purchase_price = models.IntegerField(u"precio de compra", default=0)
profit = models.IntegerField(u"lucro", default=0)
profit_margin = models.IntegerField(u"margen de lucro", default=0)
tax = models.IntegerField(u"tasa", default=0)
quantity = models.IntegerField(u"cantidad", default=0)
picture = models.ImageField(u"imagen", upload_to='product_pictures', blank=True)
group = models.ForeignKey(Group, verbose_name=u'grupo', related_name='products')
def __unicode__(self):
return self.name
I'll be appreciated if you give me a tip for creating a correct get_success_url() for this case.
Ok. I found a solution for my error. What caused the model already exists error was my ModelForm CheckProductForm. Codename attribute is unique, so my validation always returned False. What I did was to change my orginal ModelForm to a Form. This solved my whole issue. And for the form_invalid in form_valid issue. I've overwritten my form's clean_codename function to raise ValidationError if product doesn´t exist.
Here's the solution I found:
views.py
class BuyOrderCheckProduct(generic.FormView):
template_name = 'buy_order/buy_order_check_product.html'
form_class = forms.CheckProductForm
def form_valid(self, form):
product = Product.objects.get(codename=form.cleaned_data['codename'])
return redirect('buy_order_create', pk=product.pk)
forms.py
class CheckProductForm(forms.Form):
codename = forms.CharField(label=u'código')
def clean_codename(self):
try:
product = Product.objects.get(codename=self.cleaned_data['codename'])
except Product.DoesNotExist:
raise forms.ValidationError("This codename doesn't exist.")
return product
PD: Sorry for the dumb questions.