Django - get data from foreign key - python

I'm working on a Django project and attempting to create some linked models for my data which In think is working, but I cannot seem to work out how to access the linked data.
class One(models.Model)
name = models.CharField(max_length=50)
list = models.ArrayField(models.CharField(max_length=50), blank=True)
def __str__(self):
return self.name
class Many(models.Model)
name = models.CharField(max_length=50)
related = models.ForeignKey(One, null=True, blank=True)
def __str__(self):
return self.name
This is the general relationship I have set up.
What I am trying to do is, in a template have access to a list of all 'Ones', and via each of those, can access each Many and it's related attributes. I can see how to access the attributes for a single 'One', but not how to pass all of them and their related 'Many' models and the related attributes for each. Essentially the output I'd like would have a drop down list with the One's, and when this is submitted some Javascript will use the list in the 'Many' model to do some stuff.
Any advice would be much appreciated.

If you already have the objects of One model class, you can access the many objects using many_set (refer: backward relations):
{% for one_obj in one_objs %}
{% for m_obj in one_obj.many_set.all %}
# do stuff with m_obj here
{% endfor %}
{% endfor %}
One important thing to note here is that this will execute a db query for each m_obj. To make this efficient, you could prefetch the many_set with one_objs in your view.
In your view, use prefetch_related:
one_objs = One.objects.all().prefetch_related('many_set')

You can use Django's "prefetch_related" and Django's "related_name".
Also, this question has been answered here.
Though, here is what you might want, first, change your foreign key definition to this :
related = models.ForeignKey(One, null=True, blank=True, related_name='relateds')
Then you can reverse-fetch the foreign keys:
one = One.objects.get(name="TheOneYouWant").prefetch_related('relateds')
manys = one.relateds

Reverse lookups are accessible through an object's ___set attribte. So to get all the "Many" objects for a given "One" you could do one.many_set
Django reverse lookup of foreign keys
Regarding usage in a template, sets are accessible by adding "all" (since the set returns a queryset, you can run queries against it, including in the template)
Access ForeignKey set directly in template in Django
See the relevant section of the Django Documentation: https://docs.djangoproject.com/en/dev/topics/db/queries/#following-relationships-backward

Related

How can I define the ManyToManyField name in django?

I have this relationship
Class Item(models.Model):
pass
Class Category(models.Model):
items = models.ManyToManyField(Item)
I can define the field name as items for category and access it via category.items but I want to define a field name for Item too as item.categories rather than the default item.category
How can I achieve it?
Update
Tried
items = models.ManyToManyField(Item, related_name = "categories")
But I get
TypeError: Direct assignment to the reverse side of a many-to-many set is prohibited. Use categories.set() instead.
on Item.object.create(**data)
When you call Item.objects.create(), you need to omit the categories from the args. Then afterwards you can call set() to set the categories.
item = Item.objects.create()
item.categories.set(categories)
If you want to add to existing categories (rather than overwriting what’s there), call add() instead:
item = Item.objects.create()
item.categories.add(category)
Note: both add() and set() save the update to the database, so you don’t need to call item.save() afterwards
EDIT
It looks like with the default name, you can pass in a list of categories to the call to create() and it even seems like it works because you can access the list of categories in the attribute category:
category = Category.objects.create()
item = Item.objects.create(category=[category])
print(item.category)
# output: [<Category: Category object(1)>]
The problem with this is category is not how you set or access category objects on an item, it’s category_set, and that’s empty.
To see this, after running the above code fetch the item from the database, you can see that the category is not associated with the item (it wasn’t saved):
item = Item.objects.get(id=item.id)
print(item.category_set)
# output: []
(And if you try to do item.category, you get an attribute error.)
It’s confusing that Django lets you provide the category argument in a call to create() with the default related name, but fails with an error when the related_name is set. IMO it should have an error in both cases, because clearly passing an m2m list as an argument to create() does not work: you have to call set() or add().
Source: Many-to-Many relations
The thing about many to many field is not an actual field. If you take a look at the generated schema you wouldnt find the field as a column in either of the table. What happens in the back is django creates a ItemCatagory table.
class ItemCatagory(models.Model):
item = modes.ForegnKeyField(Item, related_name="catagories", on_delete... )
catagory = models.ForegnKeyField(Item, related_name="items", on_delete... )
catagory.items will give u the ItemCatagory RelatedObject and if u do catagoty.items.all() it will give u QuerySet[ItemCatagory]. so the default model.save() method woont add those rship. so u would have to overide save method on either of the models.
See, ManyToManyField can't make reverse relationship with related model as python is interpreted language, so it can't read model class of previous one. Instead, you can do one thing ...
# models.py
class Item(models.Model):
item_name = models.CharField(max_length=255, default="")
def __str__(self):
return self.item_name
class Category(models.Model):
category_name = models.CharField(max_length=255, default="")
items = models.ManyToManyField(Item)
def __str__(self):
return self.category_name
After that, you can list down your requirements in views.py file.
# views.py
def get_items_by_categories(request):
# Here, you will receive a set of items ...
get_categories = Category.objects.all()
# Filter out items with respect to categories ...
get_items_list = [{"category": each.category_name, "items": each.items} for each in get_categories]
return render(request, "categories.html", {"data": get_items_list})
Iterate your items with categories in categories.html file.
{% for each in data %}
{% for content in each %}
{{content.category}}
{% for item in content.items.all %}
{{item.item_name}}
{% endfor %}
{% endfor %}
{% endfor %}
I hope this solution will help you ..
Thank You !

Django: Splitting one app into multiple: Templates, Proxy Models & ForeignKeys

To keep my project cleaner I decided (maybe wrongly) to split my one Django app into two. One app for the management of information, the other for display. And for this I thought using Django Proxy Models in the display App would be the best way. However, I've come across a problem with the ForeignKey fields within certain models and forcing those foreign keys to use a proxy-model, instead of its originating model.
Here's some examples to make it clearer:
App_1-model.py
class Recipe(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField()
...
class Ingredient(models.Model):
name = models.CharField(max_length=200)
recipe = models.ForeignKey(Recipe)
weight = models.IntegerField()
App_2-model.py (Imports App_1 models)
class RecipeDisplayProxy(Recipe):
class Meta:
proxy = True
#property
def total_weight(self):
# routine to calculate total weight
return '100g'
class IngredientDisplayProxy(Ingredient):
class Meta:
proxy = True
#property
def weight_lbs(self):
# routine to convert the original weight (grams) to lbs
return '2lb'
App_2.views.py
def display_recipe(request, slug):
recipe = get_object_or_404(RecipeDisplayProxy, slug=slug)
return render(
request,
'display_recipe/recipe.html',
{'recipe': recipe}
)
App_2-template.html
<h2 class="display-4">{{ recipe.name }}</h2>
<p>{{ recipe.total_weight }}</p> <!-- This works fine, as expected //-->
<ul>
{% for recipe_ingredient in recipe.ingredient_set.all %}
<li>{{recipe_ingredient.ingredient}} –
{{recipe_ingredient.weight_lbs}}</li>
<!--
The above line doesn't return anything as the 'Ingredient' model, not the "IngredientDisplayProxy' is being returned. (As expected)
-->
{% endfor %}
</ul>
What's happening here is that I'm successfully returning the RecipeDisplayProxy model as specified in the view, but when I access ingredient_set it returns the Ingredient model, rather than the IngredientDisplayProxy (as expected).
So how do I force ingredient_set to return IngredientDisplayProxy models instead?
I tried implementing the code found here:
Django proxy model and ForeignKey
But had no luck. I then started digging into the init() method for RecipeDisplayProxy - to see if I could overwrite the models used in the ingredient_set, but couldn't find anything that would give me the right response.
So any ideas?
Or, am I just taking this down a bad path - and should be considering a different design altogether?
From the view you are returning the recipe instance, but in the template you are accessing the ingredient through the recipe, but it should be the other way round, from ingredient you can access the recipe.Now for the proxy model, better read this documentation
Looks like I Was doing some things wrong, and so on the advice of fips I went back and did the following:
class RecipeDisplayProxy(Recipe):
class Meta:
proxy = True
#property
def total_weight(self):
# routine to calculate total weight
return '100g'
#property
def ingredient_set(self):
qs = super(RecipeDisplayProxy, self).ingredient_set
qs.model = IngredientDisplayProxy
return qs
It was that simple :'( so thank you for the help and suggestions.

Django Incrementing Model Instance Attribute in Views w/ F()+1 and Accessing from Template

So I have a model Listing() that has a field views. In my one of my views, when someone looks at the listing's page, the views field is incremented by one via listing.views = F('views') + 1 and listing.save(update_fields=['views']). My issue is that when I access the views attribute from that same template using {{ listing.views }}, instead of display the current amount of views, the template displays F(views) + Value(1) (literally that text). Now, I assume I could use a Model method such as def get_views() which will return self.views, but I was wondering why I am getting this weird issue. Also, is there a way without writing a model method that I can get the actual integer instead of the odd F(views) + Value(1)?
Here is my current code:
models.py
class Listing(models.Model):
...
views = models.IntegerField(default=0)
listings.py
class ListingView(View):
def get(self, request):
listing_id = request.GET['listing_id']
listing = get_object_or_404(Listing, id=listing_id)
listing.views = F('views') + 1
listing.save(update_fields=['views'])
return render(request, 'listings.html', {'listing': listing})
listings.html
<html>
{{ listing.views }}
</html>
Using F expressions like this requires you to re-fetch the item once saved in order to get updated values (due to the nature of the F expression updating at a database level and not the model instance itself; perhaps that's where the decrease in operational costs come in).
From the docs -
In order to access the new value that has been saved in this way, the object will need to be reloaded:
reporter = Reporters.objects.get(pk=reporter.pk)
# Or, more succinctly:
reporter.refresh_from_db()

Django: How to return all models associated with a ForeignKey including all attributes of those models?

I have an app that I want to simply display all the URL links a page has associated with it when that page is visited.
It's similar to reddit in that there are many userpages (aka subreddits) and each page has an infinite possible amount of submitted links associated with it. The newlinkposts records are associated with a certain page via a ForeignKey.
Given a page, wow can I get all the related newlinkpost objects (including their corresponding likes, link comment, and post date) returned, in order to display them in a template?
My newlinkpost object is defined as follows:
class newlinkpost(models.Model):
newlink_tag = models.ForeignKey('userpagename') #tags link to which userpage it belongs to
link_comment = models.CharField(max_length=128) #comment to go along with post
post_date = models.DateField(auto_now=True, auto_now_add=False, null=False) #submission datestamp. later make it track editable posts if edit function is implemented
url = models.URLField(max_length = 1024, null=False) #actual submitted link
link_likes = models.IntegerField(null=False, default=0) #need to later create like button which will +1 the value
def __unicode__(self):
return self.url
When you add a ForeignKey within a model, as well as creating an attribute in the source model (in your case, newlinkpost) allowing you to find the one associated object, Django also creates a corresponding attribute inside the target model (in your case apparently userpagename).
By default this attribute is named after the source table, so in your case it will be newlinkpost_set.
That allows you to ask the question you're looking to answer: which newlinkpost objects have this userpagename?:
all_links = userpagename_instance.newlinkpost_set.all()
If you wish to apply additional filters, you can use the filter method instead:
some_links = userpagename_instance.newlinkpost_set.filter(...)
The newlinkpost_set attribute contains a RelatedManager object, which is a subtype of Manager, allowing you to use the same set of methods you could use on newlinkpost.objects, along with some additional methods allowing you to create new related objects.
Here's an example view using this technique: (this assumes you've got the model classes imported into the views module):
from django.shortcuts import render
def user_page(request, user_id):
page = userpagename.get(pk=user_id)
links = page.newlinkpost_set.all()
return render(
request,
"myapp/user_page.html",
{
page: page,
links: links,
}
)
...and here's an example of using that "links" variable in the template:
<ul>
{% for link in links %}
<li><a href="{{ link.url }}">{{ link.link_comment }} - {{ link.link_likes }} likes</li>
{% endfor %}
</ul>
You just use the reverse relationship.
my_userpagename.newlinkpost_set.all()

Extend Model's Queryset with additional attributes

I have simple Django model with some internet sessions (Radius logs). And want to show it in my template.
models.py:
class Radacct(models.Model):
radacctid = models.BigIntegerField(primary_key=True)
username = models.CharField(max_length=253)
nasipaddress = models.IPAddressField()
class Meta:
db_table = u'radacct'
view.py:
def ajax_radius_sessions(request, username):
radius_sessions = Radacct.objects.filter(username=username).order_by('-radacctid')
template.html:
{% for session in radius_sessions %}
{{ session.username }}, {{ session.nasipaddress }}</br>
{% endfor %}
In my template, I need to show the hostname based on user's ip-address (nasipaddress) as well.
Method 1:
Creation of Model's method.
I do not want to calculate Hostname as Model's method, because it will be triggered for every record, but the number of session for particular user can be very big — in this case it will cause huge amount of DNS-checks. 1000 sessions = 1000 DNS checks?..
Method 2a:
View level.
I was trying to this on View level, to check only unique IPs and get the "local" dictionary with IP-Hostname pairs:
#servers={'192.168.100.1':'alpha', '192.168.100.2':'beta', '192.168.100.3':'gamma'}
But I can not access this dictionary in the template, using the key as variable:
#Wrong!
{{ servers[session.nasipaddress] }}
Method 2b:
View level. Adding new attribute to the Queryset instance.
Maybe I can add a new attribute to my Radacct Model which is not connected with database. And fill it by hostname, in my View?
What is the proper way to calculate some "attribute" and then access it in the Template in Queryset {% for %} loop?
Again: it seems I can not do this as Model's method, so I think I should extend my database results with custom ones.
P.S. Sorry for a really looong post. This is my very first try on Stackoverflow. Thank you!
Model "method". Create a separate class that is responsible for looking up DNS entries and caching them, and refer to an instance of this class in a descriptor on the model.

Categories

Resources