django model referencing object from other class - python

Hi there pretty new to django but considering the below models, with their relationships, how do I create a read only field for the object that is a reference to a field in another class? I've looked for a while on stackoverflow, but not sure what kind of model reference that would be.
The basic logic for this being:
I have this server rack that sites on a floor in a server room, and I'm associating it to a rack position, and row to manage power consumption and other goodies. Just for my end-user's reference I want a read only field to show them what row this rack lives in, and its derived from the rack position. I'd been fiddling around with creating a method to look it up, but can't seem to figure out the syntax or find something related on the django admin pages.
Any ideas would be super appreciated, I really could use the help as I've been staring through docs forever, and can't seem to find a relevant model reference for this.
class rack(models.Model):
class Meta:
verbose_name = "Rack"
verbose_name_plural = "Racks"
def __unicode__(self):
return str(self.position)
def row(self, obj):
return self.position.row
position = models.OneToOneField("rackposition")
row = row(position.row.row)
asstag = models.CharField("Asset Tag", max_length=200, unique=True)
rackunits = models.IntegerField("Rack Units")
class rackposition(models.Model):
class Meta:
verbose_name = "Rack Position"
verbose_name_plural = "Rack Positions"
def __unicode__(self):
return str(self.position)
position = models.CharField("Position", max_length=35, primary_key=True)
row = models.ForeignKey("row")
class row(models.Model):
class Meta:
verbose_name = "Row"
verbose_name_plural = "Rows"
def __unicode__(self):
return str(self.row) + "." + str(self.suite)
row = models.CharField("Row ID", max_length=200, unique=True)
suite = models.ForeignKey(suite, blank=False)
power_budget = models.IntegerField("Power Budget")
power_volt = models.IntegerField("Power Voltage")
dual_bus = models.BooleanField("Dual Bus", default=False)

You don't need a method. Assuming you have a rack instance called my_rack, you can get its row with my_rack.position.row.
Note, you should really follow PEP8 and use CamelCase for your class names.
If you want to see it as a readonly field in the admin, you will need to define a method either on the model or on the ModelAdmin class. For example:
class RackAdmin(admin.ModelAdmin):
model = Rack
readonly_fields = ('row',)
def row(self, obj):
return obj.position.row

Related

Not sure I understand dependancy between 2 django models

I am struggling to understand django models relationship.
I have this arborescence:
A train have cars, and those cars are divided into parts. Then those parts all contains different references.
Like, for exemple, all the trains have the 6 cars, and the cars 6 parts. Each part have x reference to be associated.
I would like to use all of them in a template later on, where the user can select the train, the car and the part he worked on, then generate a table from his selections with only the references associated to the parts he selected.
It should update the train and the car (I'm trying to update a stock of elements for a company)
I dont really understand which model field give to each of them. After checking the doc, Ive done something like this but i am not convinced:
class Train(Car):
train = models.CharField(max_length=200)
id = models.CharField(primary_key='True', max_length=100)
selected = models.BooleanField()
class Meta:
abstract = True
class Car(Part):
car = models.CharField(max_length=200)
id = models.CharField(primary_key='True', max_length=100)
selected = models.BooleanField()
class Meta:
abstract = True
class Part(Reference):
part = models.CharField(max_length=200)
id = models.CharField(primary_key='True', max_length=100)
selected = models.BooleanField()
class Meta:
abstract = True
class Reference(models.Model):
reference = models.CharField(max_length=200)
id = models.CharField(primary_key='True', max_length=100)
selected = models.BooleanField()
def __str__(self):
return self.reference
Can someone please help me understand this so I can do well ? Thanks!!
1-)if you add abstract = True in your Model Meta class, your class doesn't created on database as a table. If you store data for any class, you mustn't define abstract = True.
2-)For relations, you can use models.ForeignKey . If you add a class into brackets of another class, it names: inheritance.(You can think like parent-child relation). In database management, we can use foreignkey for one-to-many relationship.
3-)In Django ORM, id field automatically generated. So you don't need to define id field.
If I understand correctly, also you want to store parts of user's selected.
So, your model can be like that:
class Train(models.Model):
name = models.CharField(max_length=200) # I think you want to save name of train
class Car(models.Model):
train = models.ForeignKey(Train,on_delete=models.Cascade)
name = models.CharField(max_length=200)
class Part(models.Model):
car = models.ForeignKey(Car,on_delete=models.Cascade)
name = models.CharField(max_length=200)
class Reference(models.Model):
part = models.ForeignKey(Part,on_delete=models.Cascade)
name = models.CharField(max_length=200)
def __str__(self):
return self.reference
#addtional table for storing user's references
class UserReference(models.Model):
user = models.ForeignKey(User,on_delete=models.Cascade)
reference = models.ForeignKey(Reference,on_delete=models.Cascade)
name = models.CharField(max_length=200)
With this definitions, you can store user's definition on UserReference table. And with Django Orm, you can access train object from UserReferenceObject.
#user_reference: UserReference object like that result of UserReference.objects.first()
user_reference.reference.part.car.train.name

Django Model Occurrence Count

I'm fairly new to Django and I'm in need of assistance with my models.
class Region(models.Model):
region_name = models.CharField(max_length=10)
def __str__(self):
return self.region_name
class Property(models.Model):
prop_name = models.CharField(max_length=200)
region_name = models.ForeignKey(Region, on_delete=models.CASCADE, verbose_name="Region")
prop_code = models.IntegerField(default=0, verbose_name="Property")
def __str__(self):
return self.prop_name
class Sale(models.Model):
prop_name = models.ForeignKey(Property, on_delete=models.CASCADE)
employee = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Person")
prop_state = models.CharField(null=True, max_length=5, choices=[('new','New'),('used','Used')])
date = models.DateField('Sale Date')
def __str__(self):
return '%s : %s %s - %s' % (self.prop_name.prop_name, self.employee, self.date, self.prop_state)
Here are my models. Property inherits from Region and Sale inherits from property. What I want to do is count the number of sales in a region and the number of sales on a specific property. However I do not know which would be the best way to approach this. I've tried using a lambda as a model field that uses the count() function but I wasn't able to see much success with that. Please let me know if you have any suggestions.
If you already have your Property/Region objects, something like this should work
sales_per_property = Sale.objects.filter(prop_name=property).count()
sales_per_region = Sale.objects.filter(prop_name__region_name=region).count()
Edit:
Seeing that you tried to add a lambda function to the model field, this may be more what you are looking for.
class Region(models.Model):
...
#property
def sales(self):
return Sale.objects.filter(prop_name__region_name=self).count()
and similarly for Property. Simply access the property using region.sales
You can annotate your querysets for Region and Property. For example:
from django.db.models import Count
regions = Region.objects.annotate(sales=Count('property__sale'))
properties = Property.objects.annotate(sales=Count('sale'))
The Regions/Propertys that arise from these querysets will have an extra attribute .sales that contains the number of related Sale objects.

Pulling several Django models together into a single list

I have a MySQL database with four related tables: project, unit, unit_equipment, and equipment. A project can have many units; a unit can have many related equipment entries. A single unit can only belong to one project, but there is a many-to-many between equipment and unit (hence the unit_equipment bridge table in the DB). I'm using Django and trying to create a view (or a list?) that shows all 3 models on the same page, together. So it would list all projects, all units, and all equipment. Ideally, the display would be like this:
Project --------- Unit ------------- Equipment
Project 1 first_unit some_equipment1, some_equipment2
Project 1 second_unit more_equipment1, more_equipment2
Project 2 another_unit some_equipment1, more_equipment1
Project 2 and_another_unit some_equipment2, more_equipment2
but at this point I'd also be happy with just having a separate line for each piece of equipment, if comma-separating them is a pain.
Although it seems straightforward to create a form where I can add a new project and add related unit and equipment data (using the TabularInline class), I cannot for the life of me figure out how to bring this data together and just display it. I just want a "master list" of everything in the database, basically.
Here's the code I have so far:
models.py
class Project(models.Model):
name = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'project'
def __str__(self):
return self.name
class Unit(models.Model):
project = models.ForeignKey(Project, models.DO_NOTHING, blank=True, null=True)
name = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'unit'
def __str__(self):
return self.name
class UnitEquipment(models.Model):
unit = models.ForeignKey(Unit, models.DO_NOTHING, blank=True, null=True)
equipment = models.ForeignKey(Equipment, models.DO_NOTHING, blank=True, null=True)
class Meta:
managed = False
db_table = 'unit_equipment'
class Equipment(models.Model):
name = models.CharField(max_length=100, blank=True, null=True)
description = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'equipment'
def __str__(self):
return self.name
views.py
def project_detail_view(request):
obj = Project.objects.all()
context = {'object': obj}
return render(request, "project/project_detail.html", context)
urls.py
urlpatterns = [
path('project/', project_detail_view),
path('', admin.site.urls),
]
admin.py
class UnitTabularInLine(admin.TabularInline):
model = Unit
extra = 0
class ProjectAdmin(admin.ModelAdmin):
inlines = [UnitTabularInLine]
class Meta:
model = Project
# a list of displayed columns name.
list_display = ['name']
# define search columns list, then a search box will be added at the top of list page.
search_fields = ['name']
# define filter columns list, then a filter widget will be shown at right side of list page.
list_filter = ['name']
# define model data list ordering.
ordering = ('name')
I think I need to somehow add more entries to the list_display in the admin file, but every time I try to add unit or equipment it throws an error. I've also tried adding more attributes to Project, but I can't seem to get the syntax right, and I'm never sure which model class I'm supposed to make it.
I've also looked at FormSets, but I cannot get my head around how to alter my current code to get it to work.
How do I get these models together into a unified view?
You don't need to edit the admin view to add your own view: which you may find you are able to do in this case to get your data displayed exactly as you want.
If you do want to show the related object values in the admin list, then you can use lookups and custom columns: however in this case your list would be based upon the Unit.
# You don't need an explicit UnitEquipment model here: you can
# use a simple ManyToManyField
class Unit(models.Model):
project = ...
name = ...
equipment = models.ManyToManyField(Equipment, related_name='units')
def equipment_list(admin, instance):
return ', '.join([x.name for x in instance.equimpent.all()])
class UnitAdmin(admin.ModelAdmin):
class Meta:
model = Unit
list_display = ['project__name', 'name', equipment_list]
def get_queryset(self, request):
return super().get_queryset(request)\
.select_related('project')\
.prefetch_related('equipment')
Note that you need to have the queryset override, otherwise there will be a bunch of extra queries as each unit also requires fetching the project and list of equipment for that unit.
There's also a further improvement you can make to your queries: you could aggregate the related equipment names using a Subquery annotation, and prevent the second query (that fetches all related equipment items for the units in the queryset). This would replace the prefetch_related()
Thanks to #Matthew Schinckel, I was able to find my way to the answer. Here's what my files look like now (only edited the Unit class in models.py):
models.py
class Unit(models.Model):
project = models.ForeignKey(Project, models.DO_NOTHING, blank=True, null=True)
name = models.CharField(max_length=255, blank=True, null=True)
equipment = models.ManyToManyField(Equipment, related_name='units')
class Meta:
managed = False
db_table = 'unit'
def __str__(self):
return self.name
def equipment_list(self):
return ', '.join([x.name for x in self.equipment.all()])
admin.py
class UnitAdmin(admin.ModelAdmin):
class Meta:
model = Unit
# a list of displayed columns name.
list_display = ('project', 'name', 'equipment_list')
# define search columns list, then a search box will be added at the top of list page.
search_fields = ['project']
# define filter columns list, then a filter widget will be shown at right side of list page.
list_filter = ['project', 'name']
# define model data list ordering.
ordering = ('project', 'name')
def get_queryset(self, request):
return super().get_queryset(request)\
.select_related('project')\
.prefetch_related('equipment')
So the changes I made were:
1. Make list_display a tuple instead of a list.
2. Throw def equipment_list(self) into the Unit class (so it's callable as an attribute of Unit) and pass (self) instead of (admin, instance) (I kept getting an error that was looking for the instance argument).

How do I set a field values of a django/mezzanine model based on the title of its foreignKey?

I would like to configure the mezzanine fork of django-filebrowser to create a subfolder when uploading an image, based on the title of a particular post within my mezzanine app.
The file field of that model requires setting "upload_to=", but I don't understand how I can make it point to a field value of its parent/foreignKey instance, rather than just a static value. I have tried defining a callable which points to exhibPost.title, as well as using it directly in the field as shown below.
I'd love to hear an explanation, I'm sure I'm misunderstanding something quite major about django here... Thanks
models.py - (imports omitted)
class exhibPost(Displayable, RichText,):
"""
An exhib post.
"""
def __unicode__(self):
return u'%s' % (self.id)
showstart = models.DateField("Show Starts")
showend = models.DateField("Show Ends")
start_time = models.TimeField(null=True, blank=True)
end_time = models.TimeField(null=True, blank=True)
summary = models.CharField(max_length=200,null=True,default=get_val)
class Meta:
verbose_name = _("exhib post")
verbose_name_plural = _("exhib posts")
ordering = ("-publish_date",)
class exhibImage(Orderable):
'''
An image for an exhib
'''
exhibPostKey = models.ForeignKey(exhibPost, related_name="images")
file = FileField(_("File"), max_length=200, format="Image",
upload_to=upload_to(
"theme.exhibImage.file",
----> exhibPost.title
)
)
class Meta:
verbose_name = _("Image")
verbose_name_plural = _("Images")
EDIT
#Anzel
The function I'm referring to is defined in my models as
def get_upload_path(instance, filename):
return os.path.join(
"user_%d" % instance.owner.id, "car_%s" % instance.slug, filename)
...and I call it in the same place that I arrowed originally.

Updating inherited attributes in django model objects

I have troubles updating attributes that are inherited from other tables
class AgentCategory(models.Model):
""" Agent Category """
class Meta:
verbose_name_plural = "agentcategories"
name = models.CharField(max_length=200, unique=True)
description = models.TextField(blank=True)
class Agent(models.Model):
agentcategory = models.ManyToManyField(AgentCategory,null=True)
How should i go about manually updating agentcategory in Agent model? As of now i am trying out this method, however, it does not work):
property_selected.agentcategory = "api/v1/agentcategory/3"
property_selected.save()
Any ideas? Thanks!
As Agent has ManyToManyField relation with AgentCategory.
agentcategory would contain the list of entries.
you can update its entries by,
agent_cats = AgentCategory.objects.filter(...)
property_selected.agentcategory.clear()
property_selected.agentcategory = agent_cats
property_selected.agentcategory.save()

Categories