I have two Django models:
class Product(models.Model):
name = models.CharField(max_length=80, null=True)
is_active = models.BooleanField(default=False, null=False)
class Image(models.Model):
url = models.CharField(max_length=255, unique=True, null=False)
product = models.ForeignKey('Product', related_name='images')
I have a specific set of products. Each product has multiple images. The initial call looks something like:
product_list = product_list.filter(is_active=True).prefetch_related('images')
The product_list then gets whittled down depending on filters that are applied.
When I try to use the product_list within the display layer (template), I iterate the list of products. I can access all the product's fields except its images.
{{ product.images.0.id }} ==> empty
{{ product.images }} ==> returns Image.None
Running the code through the debugger, I can see the Image SQL query being executed, it's just that none of the data is passed to the template. There definitely is data there, as I can verify the query running it through my SQL client. Does any one know why this is happening? How do I get access to the Images for a given product?
I solved my issue. The prefetched data had to accessed like: product.images.all.0.id
Related
I'm working on a Django Rest project where I'm given two MySQL tables:
metrics: Contain a row for each potential metric
daily_data: Contains a row for each data entry where the column names refer to metrics from the 'metrics' table
What I want to do now, is creating new entries in 'metrics' which should be automatically added to existing 'daily_data' entries (with a default value) and displayed on the website.
Here is how the current models looks like:
class Metrics(model.Model):
metric_id = models.CharField(max_length=255, primary_key=True)
is_main_metric = models.BooleanField(default=False)
name = models.CharField(max_length=255, blank=False, null=False)
description = models.CharField(max_length=255, blank=False, null=False)
lower_bound = models.FloatField(default=0.0, null=False)
upper_bound = models.FloatField(default=0.0, null=False)
class Meta:
verbose_name_plural = "Metrics"
db_table = "metrics"
class DailyData(models.Model):
location = models.CharField(max_length=255, blank=False, null=False)
date = models.DateField(blank=False, null=False)
# then a static field for each metric is added that corresponds to a 'metric_id' in the table 'metrics':
metric_01 = models.FloatField(default=0.0, null=False)
metric_02 = models.FloatField(default=0.0, null=False)
metric_03 = models.FloatField(default=0.0, null=False)
...
class Meta:
verbose_name_plural = "Daily Data"
db_table = "daily_data"
Later on, the Javascript code iterates over all 'metrics' to display them with the corresponding values from a requested 'daily_data' entry. Here is a small example:
let resp = await axios.get(`${API_URL}/daily_data/?location=berlin&date=2021-01-07`);
let data = resp.data[0];
METRICS.forEach(metric => {
let name = metric.name;
let description = metric.description;
let value = data[metric.metric_id];
$content.append(
` <div class="row">
<span>${name}:</span>
<span>${value}</span>
<span>${description}"</span>
</div> `
);
...
}
For the case that all metrics are pre-defined, the application is running fine. If I want to add a new metric, I create a new row in the database table 'metrics', then add the field manually to the 'DailyData' model from above, and finally restart the server.
However, my problem now is that I need the possibility to add new metrics dynamically. I.e. if a user adds a new metric (for example with a POST request), the metric should be added as a column to all existing 'daily_data' entries and should be displayed as an additional field on the website.
The intention is basically something like this (I know that this won't work, but just to get the idea):
def onNewMetricCreation(newMetric):
metric_id = newMetric.metric_id
new_field = models.FloatField(default=0.0, null=False)
DailyData.appendField(metric_id, new_field)
Is there a way to achieve this and add these model fields dynamically? Or is my whole data structure faulty for this case?
Edit: To solve the problem I've actually changed my data structure a bit. I've added a MetricsData model that connects the DailyData with the Metrics and contains the corresponding values. This allows each DailyData object to have a different number of metrics and new ones can be added easily.
The new models look like this:
class DailyData(models.Model):
location = models.ForeignKey("Locations", on_delete=models.CASCADE, blank=False, null=False)
date = models.DateField(blank=False, null=False)
class MetricsData(models.Model):
data_entry = models.ForeignKey("DailyData", on_delete=models.CASCADE, related_name="data_entry")
metric = models.ForeignKey("Metrics", on_delete=models.CASCADE)
value = models.FloatField(default=0.0, null=False)
class Metrics(models.Model):
metric_id = models.CharField(max_length=255, primary_key=True)
...
If I understood you correct I belive you're looking for a ForeignKey(). You would add this to your model:
class DailyData(models.Model):
metrics = models.ForeignKey(Metrics, on_delete=models.CASCADE)
Go inside django admin and I think you'll understand how ForeignKeys work. It's a reference to the metrics instance. Ps. don't add this field dynamically, that's probably impossible. But with this you can simply add another row.
So if you reference an instance of metrics. And then change that. all daily_data that references that will be "changed" since they're still referenceing the same instance.
If you need to reference more the one metrics use ManyToMany
I strongly recommend that you add a Foreign Key for DailyData to Metrics model.
class Metrics(model.Model):
...
related_day = models.ForeignKey(DailyData, on_delete=models.CASCADE, related_name="metrics", related_query_name="metrics", null=True)
Now you also need to add a signal to trigger after creating a metric to connect that metric to its related data.
#receiver(post_save, sender=Metrics)
def add_to_daily_data(sender, instance, created, **kwargs):
if created:
# Put your logic to add a specific metric to a daily data
Also, this way you can access all metrics data related to specific DailyData objects hassle-free.
daily_data.metrics.all()
I have two tables defined in my models:
class Products(models.Model):
productsid = models.AutoField(primary_key=True)
product_number = models.CharField(db_index=True, max_length=15)
title = models.CharField(max_length=255, default=None, null=True, blank=True)
date = models.DateField(db_index=True, default=datetime.date.today, null=True, blank=True)
class ProductFlag(models.Model):
product_number = models.CharField(db_index=True, max_length=15)
company = models.ForeignKey(Company, db_index=True, on_delete=models.SET_NULL, null=True)
productid = models.ForeignKey(Products, db_column='productsid', db_index=True, on_delete=models.SET_NULL, null=True)
I want to get the the 'title' from Products when I query ProductFlag.
In my views, I make this query:
productflags = ProductFlag.objects.filter(company__companyid=self.kwargs.get('company_id')).order_by('product_number')
I thought that I could then reference the title field in my template by looping through {% for product in productflags %} and
grabbing {{ product.productid.title }}, but I get nothing at all. Looking at it in the Django shell, it seems that my productid is always "None" (and my database definitely had data in productsid). I have no issue with the company foreign key. What am I doing wrong?
My apologies, I had a typo. I am referencing product.productid.title inside the loop in my template; product_number was in error. I have double-checked my database as well, and there is data in all the fields referenced.
I don't know that this will help, but in the django shell I get this behavior:
testflags = ProductFlag.objects.filter(company__companyid=43).order_by('product_number') gets a queryset of about seven. If I then try to access testflags[0].company.companyname, I get the actual company name. On the other hand, if I try to access testflags[0].productid.title, I get "AttributeError: 'NoneType' object has no attribute 'title'".
Also, if I try "testflags = ProductFlag.objects.select_related('productid').filter(company__companyid=43).order_by('product_number')" the query sees the column names in Products, but accessing them with productid, gives that AttributeError.
In pgAdmin, this query works just fine:
SELECT a.product_number, b.title
FROM productflag AS a
INNER JOIN products AS b
ON a.product_number = b.product_number
AND a.company_id = 43;
Thanks--
Al
From your snippet here:
{% for product in productflags %}
{{ product_number.productid.title }}
{% endfor %}
It looks like you're referencing the wrong variable name, because on loop, it's declared as product but you then go on to use product_number?
Try changing product_number to product or vice-versa.
Edit: Based on the new information it seems you have a typo in the filter part of your query. Try company_id rather than companyid.
.filter(company__company_id=43)
I believe the issue is in your loop. You need to access the title with
{{ product.productid.title }}
This is assuming the query set contains some data.
Thanks to everyone. You got me to look more closely at my code. It seems that my ProductFlag table was not being properly populated with the productid; so it was using a null value for its foreign key relationship with Products... No valid key = no valid data.
I have created one to many relationship (two tables) such that every user has its own IP connections list. Every user has many connections.
My models are shown below:
class Conn(models.Model):
src_ip = models.CharField(max_length=18, unique=False,default=None,blank=True,null=True)
src_port = models.CharField(max_length=6, unique=False,default=None,blank=True,null=True)
dst_ip = models.CharField(max_length=18, unique=False,default=None,blank=True,null=True)
dst_port = models.CharField(max_length=6, unique=False,default=None,blank=True,null=True)
proto = models.CharField(max_length=6, unique=False,default=None,blank=True,null=True)
start_data = models.CharField(max_length=18, unique=False,default=None,blank=True,null=True)
r_user = models.ForeignKey(User, on_delete=models.CASCADE)
class User(models.Model):
e_user = models.CharField(max_length=15, unique=False,default=None,blank=True,null=True)
e_dev = models.CharField(max_length=15, unique=False,default=None,blank=True,null=True)
e_session = models.CharField(max_length=9, unique=False,default=None,blank=True,null=True)
e_start = models.CharField(max_length=20, unique=False,default=None,blank=True,null=True)
e_stop = models.CharField(max_length=20, unique=False,default=None,blank=True,null=True)
e_summary = models.CharField(max_length=20, unique=False,default=None,blank=True,null=True)
e_ip = models.CharField(max_length=20, unique=False,default=None,blank=True,null=True)
I'm trying to get all Users with their connections (Conn) in one QuerySet and then display everything in template. So far I can display every User without any problems with
q=Users.objects.all()
and passing the QuerySet to the template.
The question may be a bit not smart but how can I query all Users including related connections (Conn) as one QuerySet and then enumerate this connections in a form?
Use prefetch_related:
users = User.objects.all().prefetch_related('conn_set')
Now for each user you can look at its conn_set and see the Conn objects linked to it. Assuming you pass users to your template as a context variable users, something like this should work:
{% for user in users %}
{{ user.e_user }}
{% for connection in user.conn_set.all }}
{{ connection.src_ip }}
{% endfor %}
{% endfor %}
Adjust fields and add other markup to suit your needs.
From the documentation, prefetch_related
Returns a QuerySet that will automatically retrieve, in a single batch, related objects for each of the specified lookups.
If this had been a one-to-one relationship or if you'd been trying to look things up in the other direction, finding all Conn objects along with the related User, you could have used select_related, which is even more efficient.
Note that you can also clean up some of your fields by choosing more appropriate field types. For example, consider GenericIPAddressField for src_ip and dst_ip.
I want to do something like
{% if "sumit" in feed.like.person.all %}
But this gives me TemplateSyntaxError. How can I do this in Djagno ?
(Basically, I want to check if 'sumit' exists in feed.like.person.all)
Here are my relevant models.
class Feed(models.Model):
name = models.CharField(max_length=120)
text = models.CharField(max_length=1200)
timestamp = models.DateTimeField(auto_now=True, auto_now_add=False)
updated = models.DateTimeField(auto_now=False, auto_now_add=True)
class Like(models.Model):
feed = models.OneToOneField(Feed)
counter = models.PositiveIntegerField()
person = models.ManyToManyField(settings.AUTH_USER_MODEL, null=True, blank=True)
I think you intended to check the following:
# check if current user likes a feed
{% if request.user in feed.like.person.all %}
But if you are checking this for multiple feeds, then this method becomes inefficient. For multiple feeds, better approach is to use Annotations as mentioned by #AKS.
Your approach to check if a user likes a feed within the templates by querying for each feed is very inefficient.
I would suggest using Conditional Expressions to annotate each feed while fetching the queryset:
from django.db.models import BooleanField, Case, When, Value
feeds = Feed.objects.all().annotate(
is_liked=Case(
When(like__person=request.user, then=Value(True)),
default=Value(False),
output_field=BooleanField()))
This way you would be getting everything in one query only. And, then in the template you can just check is_liked on the feed:
{% if feed.is_liked %}You like this.{% endif %}
I haven't really executed this query but looking at the documentation it would be something similar.
So I've been using django for a while now, and it's great. I've recently come across a little bit of a problem, and I'm sure there's a crappy way to get it to work, but what I've found with Django is that they've usually built in all sorts of mechanisms to do things for you. So what I'm not finding is this:
Here are my models:
class LandmarkGroup(models.Model):
Name = models.CharField(max_length=150)
Description = models.CharField(max_length=300, blank=True)
IsActive = models.BooleanField(default=True)
landmarks = models.ManyToManyField('Landmark', blank=True, null=True)
def __unicode__(self):
return self.Name
class Landmark(models.Model):
Name = models.CharField(max_length=150)
Description = models.CharField(max_length=300, blank=True)
Polygon = models.PolygonField()
IsActive = models.BooleanField(default=True)
objects = models.GeoManager()
def __unicode__(self):
return self.Name
I also have another model 'Team' that has a ManyToMany with LandmarkGroup, but I'm not going to post it here. I have a view where I query for all the landmarks that have a landmarkgroup that has a team with the same team id as the one I passed in:
def mobile_startup(request):
...
landmarkGroups = LandmarkGroup.objects.filter(team=device.team, IsActive=True)
landmarks = Landmark.objects.filter(landmarkgroup__team=device.team, IsActive=True)
...
return render_to_response('webservice/mobile_startup.html', {'landmarks': landmarks, 'landmarkGroups': landmarkGroups})
Everything works, the only problem I'm having is, I'm returning this all as JSON to the mobile app, and I want to provide the landmarkGroup id for the landmark, so in my template I've been trying to:
"landmarkGroup" : {{ landmark.landmarkgroup.id }} }
but that's not working. Does anyone know any way I can get the landmarkGroup ID for each landmark in my set? Do I need to extract it when I do the query? I know I can reference each landmarkGroup in the query because I can do 'landmarkgroup__team=device.team', but I need to able to reference this object in the template
LandmarkGroup.landmarksis a ManyToManyField,therefore one Landmark can belong to multiple groups.
You should be able to output them in your template like this:
{% for group in landmark.landmarkgroup_set.all %}{{ group.pk }}{% endfor %}
The first group belonging to the landmark should be accessible through {% landmark.landmarkgroup_set.all.0 %}