In my Form I want to populate a ModelChoiceField with Data from two Models.
How can I change the queryset that it contains the Objects of two different models?
My approach was to pack the querysets in a list. But that don't seems to be the right container for the data.
I don't think you can use a ModelChoiceField with two different models, because you cannot have a queryset composed by two different models.
You'd better try to use ChoiceField, which accepts a choices parameter with a list of tuples.
Say you have two models in models.py like this:
from django.db import models
class Model1(models.Model):
name = models.CharField(max_length=20, primary_key=True)
description = models.CharField(max_length=200)
class Model2(models.Model):
name = models.CharField(max_length=20, primary_key=True)
description = models.CharField(max_length=200)
You can create a Form like this in forms.py:
from django import forms
from .models import Model1, Model2
class MultipleModelChoiceForm(forms.Form):
select = forms.ChoiceField(choices=[])
def __init__(self, *args, **kwargs):
super(MultipleModelChoiceForm, self).__init__(*args, **kwargs)
choices = []
for obj1 in Model1.objects.all():
choices.append((obj1.name, obj1.description))
for obj2 in Model2.objects.all():
choices.append((obj2.name, obj2.description))
self.fields['select'].choices = choices
Note that choices are defined in the __init__ of the form, to have the select field updated with all records in Model1 and Model2 when you create an instance of the form.
Related
what I want to achieve is user will submit 3 inputs in the form 1) name 2) dropdown to select technician, 3) multiselect dropdown to select multiple products. Once the user submit the details
it will generate one lead in database with value like name,foreignkey of selected technician and id of selected products in different table. I don't know how to achieve this below I have mentioned my approch to achieve what I want. Please let me know if the models need any changes and how I can write a view for the same.
models.py
class product(models.Model):
name = models.CharField(max_length=20)
class technician(models.Model):
name = models.CharField(max_length=20)
class lead(models.Model):
name = models.CharField(max_length=20)
technician = models.ForeignKey(technician,on_delete=models.SET_NULL,null=True) #only single selection
products = models.ManyToManyField(product) #user can select multiple product in dropdown
form.py
class leadForm(form.ModelForm):
products = forms.MultipleChoiceField(queryset=Product.objects.all())
technician = forms.CharField(max_length=30,choices=[(i.id,i.name) for i in Technician.objects.all().values('id','name')
class Meta:
model = lead
fields = ('name','technician')
You should use a ModelMultipleChoiceField [Django-doc] here. The But in fact you do not need to implement the models yourself. You can simply let the Django logic do the work for you.
In order to give a textual representation at the HTML end, you can override the __str__ functions of the models:
class Product(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Technician(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Lead(models.Model):
name = models.CharField(max_length=20)
technician = models.ForeignKey(Technician, on_delete=models.SET_NULL, null=True)
products = models.ManyToManyField(Product)
Then we can simply define our form with:
class LeadForm(form.ModelForm):
class Meta:
model = Lead
fields = '__all__'
Note: usually classes are written in PamelCase and thus start with an Uppercase.
You can here use a class-based CreateView [Django-doc] for example:
from django.views.generic.edit import CreateView
from app.models import Lead
from app.forms import LeafForm
class LeadCreateView(CreateView):
model = Lead
form_class = LeadForm
template_name = 'create_lead.html'
I have the following model:
from django.db import model
class Fruit(models.Model):
fruit_name = models.CharField(max_length=100)
And A form like this:
from django import forms
from .models import SomeModel
class FruitForm(forms.ModelForm):
some_input = forms.CharField(max_length=100)
class Meta:
model = Fruit
fields = ['fruit_name']
This guide shows me how to create a dropdown like this:
fruit = [
('orange', 'Oranges'),
('cantaloupe', 'Cantaloupes'),
('mango', 'Mangoes'),
('honeydew', 'Honeydews'),
]
class TestForm(forms.Form):
some_input = forms.CharField(max_length=100)
favorite_fruit = forms.CharField(widget=forms.Select(choices=FRUIT_CHOICES))
What I want to happen is for the key/value tuple list to generate based on the fruit_id and fruit_name columns of the Fruit model, without the need to manually insert the data into the FruitForm.
What am I missing here?
Usually you do that with a ModelChoiceField [Django-doc]:
from django import forms
class TestForm(forms.Form):
some_input = forms.CharField(max_length=100)
favorite_fruit = forms.ModelChoiceField(queryset=Fruit.objects.all())
In your Fruit model, you then implement the __str__ method to decide how to render your fruit. Here that would be:
from django.db import model
class Fruit(models.Model):
fruit_name = models.CharField(max_length=100)
def __str__(self):
return self.fruit_name
You can alter the queryset= parameter, for example to filter the queryset in advance. For example if you only want to show Fruit that starts with an A, you can filter like:
from django import forms
# example: only show fruit that starts with an A
class TestForm(forms.Form):
some_input = forms.CharField(max_length=100)
favorite_fruit = forms.ModelChoiceField(
queryset=Fruit.objects.filter(fruit_name__startswith='A')
)
I have 2 models Category and Product. Category has a FK to itself, and Product a FK to Companies.
class Product(Meta):
categories = models.ManyToManyField(Category, related_name='products')
class Category(SEO, MetaData):
parent = models.ForeignKey('self', blank=True, null=True, verbose_name='parent category', on_delete=models.CASCADE)
In Django Admin in Product Create/Edit Page I need to select the Categories for Parent.
At this moment is just a long select box, with the names of Categories. I want to introduce the all path of the category-subcategory.
Ex:
Now: Category A Name
Need: Category Parent Name :: SubCategory Parent Name :: Category A Name
I don't want to modify def __str__ because is used also in other places.
You could use a custom ModelForm like this:
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = '__all__' # It's actually best to list the fields out as per Two Scoops of Django
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['categories'].label_from_instance = \
lambda obj: obj.enumerated_path
Replace enumerated_path with whatever the Product model's property/method is named. Don't forget to use the form in your ModelAdmin by adding form = ProductForm.
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
I have Django database with 2 models: DeviceModel and Device. Let's say, for example, DeviceModel object is "LCD panel" and Device object is "LCD panel №547". So these two tables have ManyToOne relationship.
class DeviceModel(models.Model):
name = models.CharField(max_length=255)
class Device(models.Model):
device_model = models.ForeignKey(DeviceModel)
serial_number = models.CharField(max_length=255)
Now I need to add some relations between DeviceModel objects. For example "LCD Panel" can be in "Tablet" object or in "Monitor" object. Also another object can be individual, so it doesn't link with other objects.
I decided to do this with ManyToMany relationship, opposed to using serialization with JSON or something like that (btw, which approach is better in what situation??).
I filled all relationships between device models and know I need to add relationship functional to Device table.
For that purpose I added "master_dev" foreignkey field pointing to 'self'. It works exactly as I need, but I want to restrict output in django admin panel. It should display only devices, that are connected through device_links. Current code:
class DeviceModel(models.Model):
name = models.CharField(max_length=255)
device_links = models.ManyToManyField('self')
class Device(models.Model):
device_model = models.ForeignKey(DeviceModel)
serial_number = models.CharField(max_length=255)
master_dev = models.ForeignKey('self', blank=True, null=True)
So, how can I limit output of master_dev field in admin panel?
There is a function "limit_choices_to", but I can't get it to work...
in forms.py:
def master_dev_chioses():
chioses = DeviceModel.objects.filter(do your connection filter here - so not all Devicemodels comes to choicefield)
class DeviceForm(forms.ModelForm):
class Meta:
model = Device
def __init__(self, *args, **kwargs):
super(Device, self).__init__(*args, **kwargs)
self.fields['master_dev'].choices = master_dev_chioses()
While there is no direct answer to my question about "limit_choices_to" function, I post solution that achieves desired output:
from django import forms
from django.contrib import admin
from .models import DeviceModel, Device
class DeviceForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DeviceForm, self).__init__(*args, **kwargs)
try:
linked_device_models = self.instance.device_model.device_links.all()
linked_devices = Device.objects.filter(device_model__in=linked_device_models)
required_ids = set(linked_devices.values_list("id", flat=True))
self.fields['master_dev'].queryset = Device.objects.filter(id__in=required_ids).order_by("device_model__name", "serial_number")
except:
# can't restrict masters output if we don't know device yet
# admin should edit master_dev field only after creation
self.fields['master_dev'].queryset = Device.objects.none()
class Meta:
model = Device
fields = ["device_model", "serial_number", "master_dev"]
class DeviceAdmin(admin.ModelAdmin):
form = DeviceForm
list_display = ('id', 'device_model', 'serial_number')
list_display_links = ('id', 'device_model')
search_fields = ('device_model__name', 'serial_number')
list_per_page = 50
list_filter = ('device_model',)