Retrieving a set of model objects that are indirectly related - python

I have 4 models:
class TransitLine(models.Model):
name = models.CharField(max_length=32)
class Stop(models.Model):
line = models.ForeignKey(TransitLine, related_name='stops')
class ExitType(models.Model):
name = models.CharField(max_length=32)
people_limit = models.PositiveSmallIntegerField()
class Exits(models.Model):
TOKEN_BOOTH = 0
GATE = 1
REVOLVING_DOOR = 2
EXIT_TYPES = (
(TOKEN_BOOTH, 'Token booth'),
(GATE, 'Gate'),
(REVOLVING_DOOR, 'Revolving door'),
)
name = models.CharField(max_length=32)
stop = models.ForeignKey(Stop, related_name='exits')
type = models.ForeignKey(ExitType, related_name='exits')
I have one TransitLine object. I want to retrieve all the unique ExitType objects that are related to the Stop objects of the TransitLine (that was a mouth full).
Some semi-pseudo code of what I want to do:
tl = TransitLine.objects.get(id=1)
exit_types = []
for s in tl.stops:
exit_types.append([e.type for e in s.exits])
unique_exit_types = list(set(exit_types))
Obviously, prefer to do this in one QuerySet call. Any suggestions?

I would try something like this:
ExitType.objects.filter(exits__stop__line=line).distinct()

Related

How do I get values from a one2many field?

I have three models which are connected with each other.
The first one is an inheriting of product.template (product_template.py) and as you see I have a one2many field which is connected to the second model showed in here.
When I want to access variables from product_template.py with dot notation I can see variable suggestion and I guess it works fine.
class ProductTemplate(models.Model):
_inherit = "product.template"
kestet_ids = fields.One2many('kestet.lines', 'product_template_id', string="Kestet", store=True)
class OnetoMany(models.Model):
_name = "kestet.lines"
partner_id = fields.Many2one("res.partner")
kestet = fields.Char(string="Emri i Kestit(n)")
vlera = fields.Integer(string="Vlera e Kestit(n)")
data = fields.Date()
sel1 = fields.Selection(
[('kontrate', 'Kontrate'), ('levrim', 'Levrim Kredie'), ('karabina', 'Karabina'), ('hipoteka', 'Hipoteka'),
('Celsat', 'Celsat'), ('Ne Perfundim', 'Ne Perfundim'),
('other', 'Tjeter')], string="Menyra Likujdimit")
pagesa1 = fields.Selection([('Cash', 'Cash'), ('Bank', 'Bank'), ('Klering', 'Klering')], string="Menyra Pageses")
product_template_id = fields.Many2one("product.template")
property_contract_id = fields.Many2one("property.contract")
Now when I try to access it from this other model property_contract.py and do the same thing as above, it doesn't give me variable suggestions and I can not access variable values.
class PropertyContract(models.Model):
_name = "property.contract"
_order = 'start_date'
name = fields.Char("Contract Number")
kestet2_ids = fields.Many2one('kestet.lines')
I also tried:
installment_id = fields.Many2one("product.templates")
installment_relation = fields.One2many(related="installment_id.kestet_ids")
It failed giving me results. I tried also the above code but didn't work...
What am I doing wrong here?

How to speed up writing in a database?

I have a function which search for json files in a directory, parse the file and write data in the database. My problem is writing in database, because it take around 30 minutes. Any idea how can I speed up writting in a database? I have few quite big files to parse, but parsing the file is not a problem it take around 3 minutes. Currently I am using sqlite but in the future I will change it to PostgreSQL.
Here is my function:
def create_database():
with transaction.atomic():
directory = os.fsencode('data/web_files/unzip')
for file in os.listdir(directory):
filename = os.fsdecode(file)
with open('data/web_files/unzip/{}'.format(filename.strip()), encoding="utf8") as f:
data = json.load(f)
cve_items = data['CVE_Items']
for i in range(len(cve_items)):
database_object = DataNist()
try:
impact = cve_items[i]['impact']['baseMetricV2']
database_object.severity = impact['severity']
database_object.exp_score = impact['exploitabilityScore']
database_object.impact_score = impact['impactScore']
database_object.cvss_score = impact['cvssV2']['baseScore']
except KeyError:
database_object.severity = ''
database_object.exp_score = ''
database_object.impact_score = ''
database_object.cvss_score = ''
for vendor_data in cve_items[i]['cve']['affects']['vendor']['vendor_data']:
database_object.vendor_name = vendor_data['vendor_name']
for description_data in cve_items[i]['cve']['description']['description_data']:
database_object.description = description_data['value']
for product_data in vendor_data['product']['product_data']:
database_object.product_name = product_data['product_name']
database_object.save()
for version_data in product_data['version']['version_data']:
if version_data['version_value'] != '-':
database_object.versions_set.create(version=version_data['version_value'])
My models.py:
class DataNist(models.Model):
vendor_name = models.CharField(max_length=100)
product_name = models.CharField(max_length=100)
description = models.TextField()
date = models.DateTimeField(default=timezone.now)
severity = models.CharField(max_length=10)
exp_score = models.IntegerField()
impact_score = models.IntegerField()
cvss_score = models.IntegerField()
def __str__(self):
return self.vendor_name + "-" + self.product_name
class Versions(models.Model):
data = models.ForeignKey(DataNist, on_delete=models.CASCADE)
version = models.CharField(max_length=50)
def __str__(self):
return self.version
I will appreciate if you can give me any advice how can I improve my code.
Okay, given the structure of the data, something like this might work for you.
This is standalone code aside from that .objects.bulk_create() call; as commented in the code, the two classes defined would actually be models within your Django app.
(By the way, you probably want to save the CVE ID as an unique field too.)
Your original code had the misassumption that every "leaf entry" in the affected version data would have the same vendor, which may not be true. That's why the model structure here has a separate product-version model that has vendor, product and version fields. (If you wanted to optimize things a little, you might deduplicate the AffectedProductVersions even across DataNists (which, as an aside, is not a perfect name for a model)).
And of course, as you had already done in your original code, the importing should be run within a transaction (transaction.atomic()).
Hope this helps.
import json
import os
import types
class DataNist(types.SimpleNamespace): # this would actually be a model
severity = ""
exp_score = ""
impact_score = ""
cvss_score = ""
def save(self):
pass
class AffectedProductVersion(types.SimpleNamespace): # this too
# (foreign key to DataNist here)
vendor_name = ""
product_name = ""
version_value = ""
def import_item(item):
database_object = DataNist()
try:
impact = item["impact"]["baseMetricV2"]
except KeyError: # no impact object available
pass
else:
database_object.severity = impact.get("severity", "")
database_object.exp_score = impact.get("exploitabilityScore", "")
database_object.impact_score = impact.get("impactScore", "")
if "cvssV2" in impact:
database_object.cvss_score = impact["cvssV2"]["baseScore"]
for description_data in item["cve"]["description"]["description_data"]:
database_object.description = description_data["value"]
break # only grab the first description
database_object.save() # save the base object
affected_versions = []
for vendor_data in item["cve"]["affects"]["vendor"]["vendor_data"]:
for product_data in vendor_data["product"]["product_data"]:
for version_data in product_data["version"]["version_data"]:
affected_versions.append(
AffectedProductVersion(
data_nist=database_object,
vendor_name=vendor_data["vendor_name"],
product_name=product_data["product_name"],
version_name=version_data["version_value"],
)
)
AffectedProductVersion.objects.bulk_create(
affected_versions
) # save all the version information
return database_object # in case the caller needs it
with open("nvdcve-1.0-2019.json") as infp:
data = json.load(infp)
for item in data["CVE_Items"]:
import_item(item)

AttributeError: 'list' object has no attribute 'objects'

I am trying to run the following which is throwing an
AttributeError: 'list' object has no attribute 'objects'
script.py
#Get Dota2 Item Rarities
dotaItemRarityUrl = 'http://api.steampowered.com/IEconDOTA2_570/GetRarities/v1?'
dotaItemRarityPayload = {'key': settings.SOCIAL_AUTH_STEAM_API_KEY,
'language': 'en',
}
dotaItemRarityInfo = requests.get(dotaItemRarityUrl, params=dotaItemRarityPayload)
dotaItemRarity = dotaItemRarityInfo.json()
dotaItemRarity = dotaItemRarity['result']['rarities']
print(dotaItemRarity)
#print(dotaItemQualities)
#Populate Database With Item Rarities that do NOT exist already
for rarity in dotaItemRarity:
print rarity
irarityId = rarity['id']
irarityProperName = rarity['localized_name']
irarityInternalName = rarity['name']
irarityColor = rarity['color']
q = dotaItemRarity.objects.filter(rarityId=irarityId)
print q
if len(q) == 0:
newRarity = dotaItemRarity(rarityId=irarityId,
rarityProperName=irarityProperName,
rarityInternalName=irarityInternalName,
rarityColor=irarityColor)
newRarity.save()
models.py
class dotaItemRarity(models.Model):
rarityId = models.IntegerField(max_length=3,primary_key=True)
rarityProperName = models.CharField(max_length=60)
rarityInternalName = models.CharField(max_length=50)
rarityColor = models.CharField(max_length=30)
def __unicode__(self):
return self.rarityInternalName
I am using south to handle migrations and have tried multiple options to fix this e.g. removing the tables and rebuilt them. As far as I can tell this should work, can anyone point me in the right direction.
dotaItemRarity is a list, and it has no objects attribute:
q = dotaItemRarity.objects.filter(rarityId=irarityId)
That's because you bound it to a list from your JSON result:
dotaItemRarity = dotaItemRarityInfo.json()
dotaItemRarity = dotaItemRarity['result']['rarities']
It is not a Django model, as you appear to expect it to be.
If you had the dotaItemRarity Django model imported into script.py, then the name is no longer bound to that model, as you replaced it with the list.
Rename the list to use a different name that doesn't mask the model.
Note that the Python style guide recommends that you use CamelCase names for classes (including Django models), to avoid such mistakes.
Following PEP 8 to refactor your code a little, as well as using some clearer naming an practices:
models.py:
class DotaItemRarity(models.Model):
rarity_id = models.IntegerField(max_length=3, primary_key=True)
rarity_proper_name = models.CharField(max_length=60)
rarity_internal_name = models.CharField(max_length=50)
rarity_color = models.CharField(max_length=30)
def __unicode__(self):
return self.rarity_internal_name
script.py:
#Get Dota2 Item Rarities
url = 'http://api.steampowered.com/IEconDOTA2_570/GetRarities/v1'
payload = {'key': settings.SOCIAL_AUTH_STEAM_API_KEY, 'language': 'en'}
response = requests.get(url, params=payload)
rarities = response.json()['result']['rarities']
for rarity in rarities:
rarity_id = rarity['id']
try:
DotaItemRarity.get(rarity_id=rarity_id)
except DotaItemRarity.DoesNotExist:
new_rarity = DotaItemRarity(
rarityId=rarity_id,
rarity_proper_name=rarity['localized_name'],
rarity_internal_name=rarity['name'],
rarity_color=rarity['color'])
new_rarity.save()

Python populating a database with one-to-many relationship

I'm a beginner, so please go easy on me. I am working on a script so that I don't have to keep entering in data when I decide to drop the database. My entire script works well, except when I'm dealing with a one-to-many relationship. It will not save to the database. Can anyone tell me what I am doing wrong or point me in the right direction?
SCRIPT:
try:
pmod.Instrument.objects.get(title='kjkjsdfsadfs')
except pmod.Instrument.DoesNotExist:
u = pmod.Instrument()
u.title = 'Bach 42 Trombone'
u.price = 550.00
u.soundDescription = 'Good'
u.functionalityDescription = 'Good'
u.damageDescription = 'Good'
u.accessoryDescription = 'Good'
u.customerName = 'Jake'
u.customerEmail = 'ks#gmail.com'
u.instrumentCategory = 1
print('Good2')
u.save()
print('Instrument1 saved')
MODEL:
class Category(models.Model):
instrumentCategory=models.CharField(max_length=50,blank=True,null=True)
def __str__(self):
return self.instrumentCategory
class Instrument(models.Model):
title = models.CharField(help_text='title',max_length=50,blank=True,null=True)
price = models.DecimalField(max_digits=8, decimal_places=2)
soundDescription=models.CharField(max_length=1000,blank=True,null=True)
functionalityDescription=models.CharField(max_length=1000,blank=True,null=True)
damageDescription=models.CharField(max_length=1000,blank=True,null=True)
accessoryDescription=models.CharField(max_length=1000,blank=True,null=True)
customerName=models.CharField(max_length=50,blank=True,null=True)
customerEmail=models.EmailField(max_length=254,help_text='Enter valid email address')
instrumentCategory=models.ForeignKey(Category)
u.instrumentCategory = 1
That's not how a models.ForeignKey field works in Django. You need to get an instance of the Category object and assign that to u.instrumentCategory.
u.instrumentCategory = pmod.Category.objects.get(id=1)
You may try :
u.instrumentCategory_id = 1

associate temporary values with appengine model querysets

The following is my model:
I have two tables match and team:
class Match(DictModel):
date_time = db.DateTimeProperty()
team1 = db.StringProperty()
team2 = db.StringProperty()
venue = db.StringProperty()
result = db.IntegerProperty()
class Team(DictModel):
tslug = db.StringProperty()
name = db.StringProperty()
matches_played = db.IntegerProperty()
matches_won = db.IntegerProperty()
rating = db.FloatProperty()
At runtime, when a post request is made to one of the handler functions, i want to dynamically associate a team rating with the queryset of Match and send the value, this is how i try to do:
matches = Match.all()
matches.filter('date_time <=', end)
matches.filter('date_time >=', start)
match_dict = functs.create_dict(matches)
self.response.out.write(match_dict)
and i have a custom function to get fetch the rating from the current team, it is as follows:
def to_dict(self):
return dict([(p, unicode(getattr(self, p))) for p in self.properties()])
def create_dict(matches):
lst = []
for m in matches:
t1 = m.team1
t2 = m.team2
te1 = Team.all().filter("name =", t1).get()
te2 = Team.all().filter("name =", t2).get()
m.setattr('rating1', te1.rating)
m.setattr('rating2', te2.rating)
lst.append(m)
data_dict = json.dumps([l.to_dict() for l in lst])
return data_dict
Trouble: i get error in setattr in place of setattr i also tried m.rating1 = te1 and m.rating2 = te2 but even that does not seem to work. Everything else is working flawlessly.
Please help thanks!
The syntax is setattr(m, 'rating1', te1.rating1); but this is no different from m.rating1 = te1.rating1.
May we see the trace back?

Categories