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)
...
Related
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.
I want to filter skill_ids fields and create 3 "abstract" fields for every SkillType, but now it's not saving this hard_skills field in admin site.
Model
class Task(models.Model):
name = models.CharField(max_length=255)
category_id = models.ForeignKey('Category', on_delete=models.RESTRICT, null=True)
level_id = models.ForeignKey('Level', on_delete=models.RESTRICT, null=True)
permission_ids = models.ManyToManyField('Permission', blank=True)
skill_ids = models.ManyToManyField('Skill', blank=True)
Form
class TaskForm(ModelForm):
hard_skills = ModelMultipleChoiceField(Skill.objects.filter(skill_type=SkillType.HARD_SKILL),
widget=FilteredSelectMultiple("Hard Skills", False), required=False)
class Meta:
model = Task
exclude = ['skill_ids']
Admin
#admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
list_per_page = 25
list_display = ['name', 'category_id', 'level_id', 'get_permissions']
list_filter = ['category_id']
filter_horizontal = ['permission_ids', 'skill_ids']
form = TaskForm
def save_model(self, request, obj, form, change):
for hard_skill in form.cleaned_data.get('hard_skills'):
obj.skill_ids.set(hard_skill)
super().save_model(request, obj, form, change)
ManyToManyField method set requires a list, so this should do:
obj.skill_ids.set(form.cleaned_data.get('hard_skills'))
This is my models.py
class InvoiceLine(AbstractSaleLine):
invoice = models.ForeignKey('books.Invoice',
related_name="lines")
name = models.ForeignKey('books.Item')
tax_rate = models.ForeignKey('books.TaxRate')
class Meta:
pass
class Item(models.Model):
item = models.CharField(max_length=255)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name="Item", default=1)
views.py
class InvoiceCreateView(generic.CreateView):
template_name = "books/invoice_create_or_update.html"
model = Invoice
form_class = InvoiceForm
formset_class = InvoiceLineFormSet
success_url = reverse_lazy("books:invoice-list")
forms.py
class InvoiceLineForm(RestrictLineFormToOrganizationMixin,
ModelForm):
class Meta:
model = InvoiceLine
fields = (
"name",
"tax_rate",
"item_id"
)
How do i filter Item the foreign key by field created_by using CBV? I am using CreateView.
You can override get_queryset, to get current user use self.request.user. To filter by related model's field use __ notation:
class InvoiceCreateView(generic.CreateView):
template_name = "books/invoice_create_or_update.html"
model = InvoiceLine
form_class = InvoiceForm
formset_class = InvoiceLineFormSet
success_url = reverse_lazy("books:invoice-list")
def get_queryset(self):
return InvoiceLine.objects.filter(name__created_by=self.request.user)
You can override the queryset for the ModelChoiceField when the form is initialised:
class InvoiceLineForm(RestrictLineFormToOrganizationMixin, ModelForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
self.fields['item'].queryset = Item.objects.filter(created_by=user)
class Meta:
model = InvoiceLine
fields = (
"name",
"tax_rate",
"item" # Note this should be `item` not `item_id`
)
You then need to pass the user to the form when you initialise the formset - something like:
formset = formset_class(form_kwargs={'user': self.request.user})
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)
I am a bit lost again. I am a beginner in this.
I have two models:
class Person(models.Model):
name = models.CharField(max_length=200, default="Name")
phone = models.CharField(max_length=12, default="+22123456789")
...
class Adress(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
city = models.CharField(max_lenght=200, default="City")
...
In my admin.py I edit them inline so on one form I can have all the person data and few of his addresses. This is important. I do need to have a relation of a single person to 1-3 addresses.
from django.contrib import admin
from .models import Adress
from .models import Person
class AdressInline(admin.TabularInline):
model = Adress
class PersonAdmin(admin.ModelAdmin):
inlines = [
AdressInline
]
admin.site.register(Person, PersonAdmin)
Now ... the problem is that there is no way to create a list_display with fields from both models. For my understanding list-display is flat, and there is no where to put 3 values of same field on it. I would like to have a list looking like this:
list_display = ('name', 'phone', 'city')
So ... my idea is to create an extra field in model Person called 'maincity' that would not be editable by user, but would be filled automatically based on the value in Adress model. After we save the form. I have found something like this in Django Docs:
def save_model(self, request, obj, form, change):
obj.user = request.user
super(ArticleAdmin, self).save_model(request, obj, form, change)
How to tell django to assign Adress.city to Person.maincity ?? *
*You might laugh, I am just learning.
Saving the City values in both Person and Address models would lead to Data Duplication. While designing models or tables, we must avoid data duplication.
We can display the values of both Person and Address models in AddressAdmin list_display itself.
Define a method nick_name in AddressAdmin class with args as self and obj. Through obj args we can get the values of foreign key fields and paste the method name in list_display.
#models.py
class Person(models.Model):
name = models.CharField(max_length=200)
nick_name = models.CharField(max_length=200)
def __str__(self):
return self.name
class Address(models.Model):
name = models.ForeignKey(Person)
city = models.CharField(max_length=200)
def __str__(self):
return self.name
#admin.py
class AddressAdmin(admin.ModelAdmin):
list_display = ('nick_name', 'city')
def nick_name(self, obj):
return obj.name.nick_name