I have a many to many relationship model which actually shows as a multi select list on forms. In one particular place I want to show it as dropdown single selection - any idea how to do this?
See the documentation on overriding default field types or widgets.
If you've got a Book model, with a ManyToMany relationship to Author, like this:
class Author(models.Model):
name = models.CharField(max_length=100)
title = models.CharField(max_length=3, choices=TITLE_CHOICES)
def __unicode__(self):
return self.name
class Book(models.Model):
name = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
then you can do something like this:
from django.forms import ModelForm, Select
class AuthorForm(ModelForm):
class Meta:
model = Author
widgets = {
'name': Select(),
}
NB. This code is not tested, but will hopefully be enough to get you on your way.
Related
So I have a ManyToMany field in my model and Django admin renders it as a multiselect field. It works fine and I have no issues — except that I can't Edit it after creating a record.
I tried Del key, mouse right-click, nothing worked. Looks like I have to delete the record and create it again?
This is the field I want to edit. I want to remove one or two of the above items. I'm on Windows.
Well it looks like there's a simpler solution:
(Courtesy of Webdev Hints)
Here's my models:
class Technology(models.Model):
title = models.CharField(max_length=10)
def __str__(self):
return self.title
class Meta:
verbose_name_plural = 'Technologies'
class Project(models.Model):
title = models.CharField(max_length=100)
description = HTMLField()
technology = models.ManyToManyField(Technology, related_name='projects')
image = models.ImageField(upload_to='projects/')
def __str__(self):
return self.title
And the solution is to add the following to the admin.py:
#admin.register(Technology)
class TechnologyAdmin(admin.ModelAdmin):
pass
class TechnologyInline(admin.TabularInline):
model = Project.technology.through
#admin.register(Project)
class ProjectAdmin(admin.ModelAdmin):
inlines = (TechnologyInline,)
exclude = ('technology',)
Now the ManyToMany filed is editable.
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 models for adding products. The name of the products are in several languages, so I made a on-to-many raltion with a 'Name'-model.
This is my models
class Product(models.Model):
active = models.BooleanField()
class ProductName(models.Model):
productName = models.CharField(max_length=250)
product = models.ForeignKey('Product', on_delete=models.CASCADE)
language = models.ForeignKey('Language', on_delete=models.CASCADE)
def __str__(self):
return self.productName
class Language(models.Model):
language = models.CharField(max_length=55)
languageAbbreviation = models.CharField(max_length=10)
def __str__(self):
return self.language
Now in the admin page of mysite, I want to add product names on creation of a product.
I tried some misarable attempt with some thing I found about 'admin.TabularInline'. But I think that is wrong because nothing is working with that.
Any suggestion about how to solve this is much appreciated!
A model admin like this:
class ProductAdmin(admin.ModelAdmin):
class ProductNameInline(admin.TabularInline):
model = ProductNameInline
fields = ['productName', 'language']
model = Product
inlines = [ProductNameInline]
should provide you with a page that allows you set the name(s) of a product.
Make sure all the necessary static files for the javascript are available.
I have the following models:
class Student(models.Model):
class Meta:
app_label = 'ground'
name = models.CharField(max_length=255)
def __unicode__(self):
return unicode(self.name)
class Program(models.Model):
class Meta:
app_label = 'ground'
name = models.CharField(max_length=255)
student = models.ManyToManyField(Student)
def __unicode__(self):
return unicode(self.name)
And the following admin:
class ProgramAdmin(admin.ModelAdmin):
formfield_overrides = {
models.ManyToManyField: {
'widget': admin.widgets.FilteredSelectMultiple(
Student._meta.verbose_name_plural, False)
}
}
admin.site.register(Program, ProgramAdmin)
As you can see I use django's FilteredSelectMultiple to display a nice select field for the Program admin where I can select multiple students at once.
Problem
I have over 2500 students in the database. The browser has problems with rendering all students into the select field. Is there a way to overcome this problem. Like doing stuff with javascript
It's not a browser problem. Django is fetching a list of data of all the students on every single load. If you keep it that way, it will be slow and slower. You can check out raw_id_fields.
I'm currently writing my own autocomplete widget... Stay tuned!
Suppose I have the following models in models.py:
class Category(models.Model):
name = models.CharField(max_length=128)
parent = models.ForeignKey('self', null=True, blank=True)
class Item(models.Model):
name = models.CharField(max_length=128)
categories = models.ManyToManyField(Category, related_name='categories')
class Membership(models.Model):
category = models.ForeignKey(Category)
item = models.ForeignKey(Item)
date_assigned = models.DateField()
And, as documented in Working with many-to-many intermediary models, I have this in admin.py:
class MembershipInline(admin.TabularInline):
model = Membership
extra = 3
class CategoryAdmin(admin.ModelAdmin):
inlines = (MembershipInline,)
class ItemAdmin(admin.ModelAdmin):
inlines = (MembershipInline,)
When creating an Item in the admin site, category inline forms are indeed rendered, but as mere <select> widgets, which is somewhat cumbersome, as each category is shown at the same level.
How could I get these rendered as trees? I don't need something very fancy and some indentation within the select would be just fine.
I tried with django-mptt and django-treebeard, but they don't seem to have the inline intermediary many-to-many model case covered.
If you register the Item model with mptt. You can overwrite the unicode method:
class Item(models.Model):
...
def __unicode__(self):
return '%s%s' % (' ' * self.level, super(MarkerCategory, self).__unicode__())
If you want a cleaner solution, you should overwrite the form, and change the widget and to do this logic in this specific widget.
I hate when browsers are "smart".
from django.utils.safestring import mark_safe
...
class Item(models.Model):
...
def __unicode__(self):
return mark_safe('%s%s' % (' ' * 4 * self.level,
super(Item, self).__unicode__()))
But I recomend that this logic is in a specify widget