I got a model where a "student" is able to enrol to a "course". The issue is that courses may have the same name(but different attendance, 'full time' or 'part time'). So as you can see in the image attached, I would like to know which course a student is enrolled to. Either inside the select box or next to it. I tried to use a list display but couldn't figure out how to use it.
Edit: the field that the type of attendance is stored is called 'attendance' and it's part of the class 'Course'.
admin.py
class StudentAdmin(admin.StackedInline):
model = Student
verbose_name_plural = 'students'
# Define a new User admin
class UserAdmin(UserAdmin):
inlines = (StudentAdmin, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
On the Course model you can define the __unicode__(self): method to interpolate two attributes together. So, in your case you could combine the name of the course and the attendance.
That would look something like this:
class Course(models.Model):
name = models.CharField(max_length=100)
attendance = models.CharField(max_length=100)
def __unicode__(self):
return "%s (%s)" % (self.name, self.attendance)
Then the string representation of that course will be the name of the course followed by the attendance in parenthesis.
An example of this can be found in the documentation.
Related
I have 2 models that look like this:
models.py
class Client(models.Model):
deal = models.ManyToManyField('Deal', related_name="clients")
class Deal(models.Model):
client = models.ManyToManyField(Client, related_name="deals")
Then in the admin, I have inlined the related models to make it easy to make changes regardless of the object type you have open.
admin.py
class ClientInline(admin.TabularInline):
model = Deal.client.through
class DealAdmin(admin.ModelAdmin):
inlines = [ClientInline]
class DealInline(admin.TabularInline):
model = Client.deal.through
class ClientAdmin(admin.ModelAdmin):
inlines = [DealInline]
However, if you add a Client to a Deal and then open the Client detail page, the corresponding deal does not appear. Is there something I'm not connecting?
It is enough to have relation define only in one model. Otherwise you'll have 2 separate tables for separate ManyToMany relation: ClientDeal and DealClient.
What you need to do is to choose which one you need to leave. And probably update Admin inlines according to Django Admin documentation
class Client(models.Model):
deals = models.ManyToManyField('Deal', related_name="clients")
class Deal(models.Model):
pass
Yes, If you're using models.manytoMany() , you have to put it only in one model. no the two
But there's a very good attribute you should use: through
with through attribute you can create a intermediate model. here there's an example:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=200)
groups = models.ManyToManyField('Group', through='GroupMember', related_name='people')
class Meta:
ordering = ['name']
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=200)
class Meta:
ordering = ['name']
def __unicode__(self):
return self.name
class GroupMember(models.Model):
person = models.ForeignKey(Person, related_name='membership')
group = models.ForeignKey(Group, related_name='membership')
type = models.CharField(max_length=100)
def __unicode__(self):
return "%s is in group %s (as %s)" % (self.person, self.group, self.type))
later, you can use your inline admin class!
I just tested this an you were actually really close.
First, #wowkin2 said, you don't want to define a ManyToManyField in both models so I would probably just define it in your Deal model.
Second, replace this:
class DealInline(admin.TabularInline):
model = Client.deal.through
with this:
class DealInline(admin.TabularInline):
model = Deal.client.through
And everything should work.
So, this is what your files should now look like:
models.py
class Deal(models.Model):
client = models.ManyToManyField(Client, related_name="deals")
admin.py
class ClientInline(admin.TabularInline):
model = Deal.client.through
class DealAdmin(admin.ModelAdmin):
inlines = [ClientInline]
class DealInline(admin.TabularInline):
model = Deal.client.through
class ClientAdmin(admin.ModelAdmin):
inlines = [DealInline]
I am displaying data in Django admin panel for many to many relationship tables. I got None instead of a list of names.
I am using:
Python: 3.6
Django: 2.2
List_display for ManytoMany fields in Admin panel
I had also already asked a related question on this topic. That being said I changed my model since then (I also got not answers).
models:
class Assessment(models.Model):
name = models.CharField(max_length=255)
class Participant(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
first_name = models.CharField(max_length=255,blank=True,null=True)
class Seminar(models.Model):
topic = models.CharField(max_length=255)
assessment = models.ManyToManyField(Assessment,blank=True)
participant = models.ManyToManyField(Participant,blank=True,through='SeminarParticipant',through_fields=('seminar','participant'))
class SeminarParticipant(models.Model):
seminar = models.ForeignKey(Seminar,blank=True,on_delete=models.CASCADE)
participant = models.ForeignKey(Participant,blank=True,on_delete=models.CASCADE)
request_time = models.PositiveIntegerField(default=0,validators=[MinValueValidator(0),])
is_survey_completed = models.BooleanField(default=False)
admin:
#admin.register(Seminar)
class SeminarAdmin(admin.ModelAdmin):
list_display = ('topic''assessment')
def assessment(self,obj):
return "\n".join([item for item in obj.assessment.all()])
I was expecting name of assessment in list_display of seminar admin but got assessments.Assessment.None in list_display of admin panel.
Snapshot of output:
Thank you very much for your help
assessment is the name of the field, which will be found before your method. Call the method something else and use that name in list_display.
class SeminarAdmin(admin.ModelAdmin):
list_display = ('topic', 'assessment_list')
def assessment_list(self, obj):
return "\n".join([item for item in obj.assessment.all()])
(Although note, assessment is an odd name for a field that contains many assessments; you should think about renaming the field itself.)
I have a model with two entities, Person and Code. Person is referenced by Code twice, a Person can be either the user of the code or the approver.
What I want to achieve is the following:
if the user provides an existing Person.cusman, no further action is needed.
if the user provides an unknown Person.cusman, a helper code looks up other attributes of the Person (from an external database), and creates a new Person entity.
I have implemented a function triggered by pre_save signal, which creates the missing Person on the fly. It works fine as long as I use python manage.py shell to create a Code with nonexistent Person.
However, when I try to add a new Code using the admin form or a CreateView descendant I always get the following validation error on the HTML form:
Select a valid choice. That choice is not one of the available choices.
Obviously there's a validation happening between clicking on the Save button and the Code.save() method, but I can't figure out which is it. Can you help me which method should I override to accept invalid foreign keys until pre_save creates the referenced entity?
models.py
class Person(models.Model):
cusman = models.CharField(
max_length=10,
primary_key=True)
name = models.CharField(max_length=30)
email = models.EmailField()
def __unicode__(self):
return u'{0} ({1})'.format(self.name, self.cusman)
class Code(models.Model):
user = models.ForeignKey(
Person,
on_delete=models.PROTECT,
db_constraint=False)
approver = models.ForeignKey(
Person,
on_delete=models.PROTECT,
related_name='approves',
db_constraint=False)
signals.py
#receiver(pre_save, sender=Code)
def create_referenced_person(sender, instance, **kwargs):
def create_person_if_doesnt_exist(cusman):
try:
Person = Person.objects.get(pk=cusman)
except Person.DoesNotExist:
Person = Person()
cr = CusmanResolver()
Person_details = cr.get_person_details(cusman)
Person.cusman = Person_details['cusman']
Person.name = Person_details['name']
Person.email = Person_details['email']
Person.save()
create_Person_if_doesnt_exist(instance.user_id)
create_Person_if_doesnt_exist(instance.approver_id)
views.py
class CodeAddForm(ModelForm):
class Meta:
model = Code
fields = [
'user',
'approver',
]
widgets = {
'user': TextInput,
'approver': TextInput
}
class CodeAddView(generic.CreateView):
template_name = 'teladm/code_add.html'
form_class = CodeAddForm
You misunderstood one thing: You shouldn't use TextField to populate ForeignKey, because django foreign keys are populated using dropdown/radio button to refer to the id of the object in another model. The error you got means you provided wrong information that doesn't match any id in another model(Person in your case).
What you can do is: not using ModelForm but Form. You might have some extra work to do after you call form.is_valid(), but at least you could code up your logic however you want.
I have a person model with different attributes. A person has an Address, which is a OneToOneField for the Person/parent model. When i go for admin form in adding/editing the person, i also need to edit the address attributes like line1, line2, mobile and so on. Similarly, i want to delete some person from the admin page and it should delete the matching references like address as well. I have already tried lots of things, including Inline and all. But, i think inline could have worked only if the address would be having a foreign key with the person, reverse was not possible. Any kind of help on this would be highly appreciated...
class Person(models.Model):
person_Id = models.CharField(max_length=32L, primary_key=True, db_column='PERSON_ID', editable=False)
business_Address = models.OneToOneField(Address, unique=True, db_column='BUSINESS_ADDRESS_ID')
class Meta:
db_table = 'PD_PERSON'
class Address(models.Model):
# Field names made lowercase.
address_id = models.IntegerField(primary_key=True, db_column='ADDRESS_ID')
address_name = models.CharField(max_length=256L, db_column='NAME', blank=True)
line1 = models.CharField(max_length=128L, db_column='LINE1', blank=True)
class Meta:
db_table = 'PD_ADDRESS'
In admin.py ~
class PersonInline(admin.TabularInline):
model = Person
class PersonAdmin(admin.ModelAdmin):
list_display = ('customer_Id', 'complete_Name', 'company')
search_fields = ('name', 'customer_Id', 'email_Id')
class AddressAdmin(admin.ModelAdmin):
inlines = [ PersonInline, ]
admin.site.register(Address, AddressAdmin)
#admin.site.register(Category)
#admin.site.register(Languages)
admin.site.register(Person, PersonAdmin)
Problem 1: delete adress on person deletion
You can use post_delete signal to automatically delete the adress once your person is deleted. It's quite simple to use
from django.db.models.signals import post_delete
from web.programing.roxx.models import Person, Adress
#receiver(post_delete, sender=Person, dispatch_uid="person_post_delete")
def onPersonDelete(sender, instance, using, **kwargs):
# be carefull, the instance doesn't exist anymore in the database
adress_id = instance.business_Address.id
if id != None:
Adress.objects.filter(id=adress_id).delete()
But this won't display the adress in the "Are you sure ?" message..
Problem 2: edit the adress in the person admin page
For the moment, I don't see any solution simpler than building a custom form to edit Person model, and override both add_view() and change_view() methods to initialize this form withe the O2O key..
I've got a weird problem in django admin list_display. Whenever I add a foreign key to a list_display the whole change list view goes blank showing only the total no of entries.
models.py:
class Organization(models.Model):
org_id = models.AutoField(primary_key=True)
org_name = models.CharField(max_length=288)
def __unicode__(self):
return self.org_name
class Meta:
db_table = u'organization'
class Server(models.Model):
server_id = models.AutoField(primary_key=True)
server_name = models.CharField(max_length=135,verbose_name="Server Name")
org = models.ForeignKey(Organization,verbose_name="Organization")
def __unicode__(self):
return self.server_name
class Meta:
db_table = u'server'
admin.py:
class ServerAdmin(admin.ModelAdmin):
list_display = ('server_name','org')
admin.site.register(Server,ServerAdmin)
Now I'd expect this code to show me the organization name in the ChangeList View, But instead I get this:
If I remove the org in the list_display of ServerAdmin class, I get this:
I didn't modify the template or override any ModelAdmin methods. I'm using Mysql(5.1.58) as my database that comes with ubuntu 11.10 repository.
I'll be really glad if I could a get a sloution for this problem guys. Thanks in advance.
I second Stefano on the fact that null=True, blank=True is to be added. But, I think you only need to add it to the org_name field of the Organization model. That should make your way through. It has to be done because you have run inspectdb to create models from your legacy DB. And probably the organization table in the DB has an empty string stored. So, adding the above would allow the Admin to have a blank field/column displayed.
Moreover, you can also try using callbacks in situations where you don't want to make changes to your model definition like the above.
Try adding null=True, blank=True to all your model fields.
Usually django admin will silenty fail (thus show no records in the list) if the row does not validate the model constraints.
See: https://stackoverflow.com/a/163968/1104941
Does the following work for you?
admin.py:
class ServerAdmin(admin.ModelAdmin):
list_display = ('server_name','org__org_name')
admin.site.register(Server,ServerAdmin)
I had a similar problem and solved it like this (using your example):
class ServerAdmin(admin.ModelAdmin):
list_display = ('server_name', 'get_org')
def get_org(self, obj):
return obj.org.org_name
get_org.short_description = 'Org'
admin.site.register(Server,ServerAdmin)