Django Many2many field Attribute Error - python

I'm getting an unexpected error when trying to add data to a many2many field in a django model. I'm not sure why I'm not getting the related data (payment_method)
For the models below
class PaymentMethod(models.Model):
MONTHLY_DIRECT_DEBIT = 'MDD'
CASH_OR_CHEQUE = 'CAC'
QUARTERLY_DIRECT_DEBIT = 'QDD'
PAYMENT_CHOICES = (
(MONTHLY_DIRECT_DEBIT, 'Monthly Direct Debit'),
(CASH_OR_CHEQUE, 'Cash or Cheque'),
(QUARTERLY_DIRECT_DEBIT, 'Quarterly Direct Debit'),
)
unique_id = models.CharField(max_length=3, choices=PAYMENT_CHOICES)
name = models.CharField(max_length=255, unique=True)
class Tariff(models.Model):
name = models.CharField(max_length=255)
supplier = models.ForeignKey(Supplier)
payment_method = models.ManyToManyField(PaymentMethod)
region = models.ManyToManyField(Region)
class Meta:
unique_together = ('name', 'supplier')
class Supplier(models.Model):
unique_id = models.IntegerField(unique=True)
name = models.CharField(max_length=255, unique=True)
I am trying to add the payment method data to the tariff via:
supplier = Supplier.objects.get(unique_id=region.default_supplier.unique_id)
payment_method_instance = PaymentMethod.objects.get(unique_id=payment_method['id'])
tariff, created = Tariff.objects.get_or_create(name=tariff, supplier=supplier)
if created:
sys.stdout.write('Tariff {} not found for Supplier: {}\n'.format(tariff, supplier))
tariff.payment_method.add(payment_method_instance)
when I try to access the tariff's payment method via the below I get the below error:
>>> tariff1.payment_method
Out[4]: <django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager at 0x11326c710>
>>> tariff.payment_method.unique_id
Traceback (most recent call last):
File "", line 3066, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-5-a0370f309c95>", line 1, in <module>
tariff.payment_method.unique_id
AttributeError: 'ManyRelatedManager' object has no attribute 'unique_id'

The payment_method field in Tariff model, is a M2M field, so it won't return an object, it will return a Set of objects. So, you can not access to .unique_id attr because it's not an PaymentMethod object. You could get the unique_id value for all the PaymentMethod objects related with your current Tariff object:
>>> tariff1.payment_method.all().values_list('unique_id', flat=True)

Related

Django: Non-primary Foreign Key object can't access related model instance

I'm new in Django. I have 2 class tech_system adn equiptment in models.py
class tech_system(models.Model):
id_tech_system = models.BigAutoField(db_column='ID_tech_system', primary_key=True)
system_descript_short = models.CharField(max_length=255, blank=True, null=True)
#More field here
tech_system_code = models.CharField(unique=True, max_length=40)
class Meta:
managed = False
db_table = 'tech_system'
def __str__(self):
return self.system_descript_short
class equiptment(models.Model):
id_thietbi = models.BigAutoField(db_column='ID_thietbi', primary_key=True)
tech_system_code = models.ForeignKey('tech_system', models.DO_NOTHING, db_column="tech_system_code", blank=True, null=True)
class Meta:
managed = False
db_table = 'equiptment'
I use python shell, equiptment model object can't access to related tech_system model instance. I got the error matching query does not exist.
I want to get the value obj1.equiptment.tech_system_code.system_descript_short. How can I do?
Thank you.
>>> obj1 = equiptment.objects.first()
>>> obj1.tech_system_code_id
'530'
>>> obj1.tech_system_code
Traceback (most recent call last):
File "D:\Dev1\env1\lib\site-packages\django\db\models\fields\related_descriptors.py", line 173, in __get__
rel_obj = self.field.get_cached_value(instance)
File "D:\Dev1\env1\lib\site-packages\django\db\models\fields\mixins.py", line 15, in get_cached_value
return instance._state.fields_cache[cache_name]
KeyError: 'tech_system_code'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "D:\Dev1\env1\lib\site-packages\django\db\models\fields\related_descriptors.py", line 187, in __get__
rel_obj = self.get_object(instance)
File "D:\Dev1\env1\lib\site-packages\django\db\models\fields\related_descriptors.py", line 154, in get_object
return qs.get(self.field.get_reverse_related_filter(instance))
File "D:\Dev1\env1\lib\site-packages\django\db\models\query.py", line 437, in get
self.model._meta.object_name
app.models.tech_system.DoesNotExist: tech_system matching query does not exist.
I found that tech_system_code record is assigned as str type, not an object in equiptment model. Record values imported to the database is not correct.
Thanks for your help.

how can i access to the intermediary table fields through the parent table in django?

this is my tables:
class MyUser(models.Model):
pass
class Product(models.Model):
name = models.CharField()
class Order(models.Model):
customer = models.ForeignKey(MyUser, on_delete=models.CASCADE)
products = models.ManyToManyField(Product , through='MidCartProduct')
class MidCartProduct(models.Model):
class Meta :
unique_together = (('order_id' , 'product_id'))
order_id = models.ForeignKey(Order , on_delete=models.CASCADE)
product_id = models.ForeignKey(Product , on_delete=models.CASCADE)
quantity = models.PositiveSmallIntegerField()
and so how can i access to the intermediary table quantity field through the Order table?
is that possible?
i tried to do this but not working :
>>> a = Order.objects.first().products.first()
>>> a.quantity
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Order' object has no attribute 'quantity'
Sorry for my bad English
You iterate over the midcartproduct_set instead:
myorder = Order.objects.first()
for midcartproduct in myorder.midcartproduct_set.all():
print(midcartproduct.quantity, midcartproduct.product_id)
Since you likely need all Products, you better add a .select_related(…) [Django-doc] for that:
myorder = Order.objects.first()
for midcartproduct in myorder.midcartproduct_set.select_related('product_id'):
print(midcartproduct.quantity, midcartproduct.product_id)
Note: Normally one does not add a suffix _id to a ForeignKey field, since Django
will automatically add a "twin" field with an _id suffix. Therefore it should
be product, instead of product_id.

How can I fix "ValueError: "<Klant: None - aCustomer>" needs to have a value for field "klant" before this many-to-many relationship can be used."

Here are the imports:
from django.db import models
from datetime import datetime, timedelta
from django.contrib.auth.models import User
This is the first class I defined. It is the status of the action (Actie) and it has a status-id and a status-name with a max_length attribute of 5 (todo, doing, done)
class Status(models.Model):
id = models.IntegerField(primary_key=True)
status_naam = models.CharField(max_length=5, default='todo')
def __str__(self):
return str(self.id) + " - " + self.status_naam
This is the second class Klant which means customer. It has an id, a customer-name and users from the customer which is a ManyToManyField referring to users from the the User-table django gives me.
class Klant(models.Model):
id = models.IntegerField(primary_key=True)
klant_naam = models.CharField(max_length=100, default='-')
klant_gebruiker = models.ManyToManyField(User)
def __str__(self):
return str(self.id) + " - " + self.klant_naam
This is the class Actie (Action or the action the user determines) which has an id, an action-name, a action-status which refers to the table Status here above, an action-publish-date, an ending-date (the deadline) and a customer-id which refers to Klant.
class Actie(models.Model):
id = models.IntegerField(primary_key=True)
actie_naam = models.CharField(max_length=150, default='-')
actie_status = models.ForeignKey(Status, default=1)
actie_aanmaakdatum = models.DateTimeField(default=datetime.now())
actie_einddatum = models.DateTimeField(default=datetime.now() + timedelta(days=1))
actie_klant = models.ForeignKey(Klant, default=1)
def __str__(self):
return str(self.id) + " - " + self.actie_naam
This is what I'm doing in the shell:
(InteractiveConsole)
>>> from MyApp.models import User, Klant
>>> klant1 = Klant.objects.create(klant_naam='aCustomer')
>>> user1 = User.objects.get(username='peterdevries')
>>> klant1.klant_gebruiker.add(user1)
Traceback (most recent call last):
File "C:\shell.py", line 69, in handle
self.run_shell(shell=options['interface'])
File "C:\shell.py", line 61, in run_shell
raise ImportError
ImportError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "C:\related_descriptors.py", line 468, in __get__
return self.related_manager_cls(instance)
File "C:\related_descriptors.py", line 751, in __init__
(instance, self.source_field_name))
ValueError: "<Klant: None - aCustomer>" needs to have a value for field "klant" before this many-to-many relationship can be used.
>>>
So my question now is what do I have to do to fix this ValueError?
Your models look like they were autogenerated via inspectdb. The problem is that all your primary keys are IntegerFields. This means that Django does not know that the database will give them a value automatically, so does not update the field on creation with that value - and therefore can't create the many-to-many relation, which requires inserting pks from both sides into the linking table.
You could change the fields to AutoField, but the simplest solution is just to delete those definitions altogether - Django will automatically set the primary key to an AutoField named id if another pk is not defined.

Django - Select related or join on two different base models

I have two base models SiteData and Showoroom Service that have model structure as per the below.
I need the SiteData info but I also would like to get link_type from the showroomservice model if there is a matching ID.
ive tried a few things thus far, none are getting what I need, what is the best way to achieve this?
Thanks
select related
>>> nd = ShowroomService.objects.select_related('site').all()
>>> nd
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 229, in __repr__
return '<%s %r>' % (self.__class__.__name__, data)
File "/usr/local/lib/python3.6/site-packages/django/db/models/base.py", line 590, in __repr__
u = six.text_type(self)
TypeError: __str__ returned non-string (type SiteData)
Combining :
>>> complete_data = site_data | monitoring_data
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 310, in __or__
combined.query.combine(other.query, sql.OR)
File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/query.py", line 529, in combine
"Cannot combine queries on two different base models."
AssertionError: Cannot combine queries on two different base models.
chaining
>>> final_data = chain(monitoring_data, site_data)
>>> for i in final_data:
... '{} {}'.format(i.location,i.link_Type)
...
Traceback (most recent call last):
File "<console>", line 2, in <module>
AttributeError: 'ShowroomService' object has no attribute 'location'
sites.models.py
class SiteData(models.Model):
location = models.CharField(max_length=50)
site_type = models.ForeignKey(SiteTypes, verbose_name="Site Type", \
on_delete=models.PROTECT)
subnet = models.GenericIPAddressField(protocol='IPv4')
routed_subnet = models.GenericIPAddressField(protocol='IPv4', \
verbose_name="Routed Link Subnet", blank=True, null=True)
bgp_as = models.CharField(max_length=6, verbose_name="BGP AS Number")
opening_date = models.DateField(verbose_name="Showroom opening date")
last_hw_refresh_date = models.DateField(verbose_name="Date of latest hardware refresh", \
blank=True, null=True)
is_live = models.BooleanField(default=False, verbose_name="Is this a live site?")
tel = models.CharField(max_length=20, blank=True, null=True)
address = models.CharField(max_length=255, blank=True, null=True)
town = models.CharField(max_length=255, blank=True, null=True)
...
class Meta:
verbose_name = "Site Data"
verbose_name_plural = "Site Data"
ordering = ('location',)
permissions = (
("can_view", "Can View"),
("can_view_mgmt", "Can View Management"),
)
def __str__(self):
return self.location
monitoring.models.py
from sites.models import SiteData
class ShowroomService(models.Model):
site = models.ForeignKey(SiteData, verbose_name="Site", \
on_delete=models.PROTECT)
link_type = models.CharField(max_length=200, blank=True, null=True)
preference = models.CharField(max_length=200, blank=True, null=True)
timestamp = models.DateTimeField(auto_now_add=True, blank=True, null=True)
dashboard = models.BooleanField(default=True, verbose_name="display on monitoring dashboard?")
class Meta:
verbose_name = "Showroom Service Data"
verbose_name_plural = "Showroom Service Data"
def __str__(self):
return self.site
You can pull all related objects using django's "related manager". Documentation is available here: https://docs.djangoproject.com/en/2.0/ref/models/relations/#related-objects-reference
By calling showroom_service_set on a SiteData model, you can get the set of child records for each SiteData.
Just to explain why the listed attempts failed as well:
your __str__ methods on your models need to return strings. If they don't, you'll get that exception.
The pipe operator | is used for ORing together queries in django. That chunk of code is attempting to combine a query for 2 different types of models.
With the chain attempt, you've created a list containing two different types of models. One of which doesn't have a location attribute.
Here's a chunk of code to get the link type for all ShowroomService models attached to a SiteData model:
for site_data in SiteData.objects.all():
for showroom in site_data.showroom_service_set.all():
print showroom.link_type
I'm not sure how django handles camel-casing with related objects, so showroom_service_set is my best guess. You might have to do a little leg work on this to figure out what the actual set is called.
EDIT: There's something called prefetch_related. Here's a SO answer about it; I think it'll get you what you're looking for: https://stackoverflow.com/a/13096423/769971

csv import to manytomanyfield Django

I used below code to import csv file to django model containing manytomanyfield Release.metamodules
>>> from app.models import Metamodule,Release
>>> reldata = csv.reader(open('/root/Django-1.6.5/django/bin/dashboard/release.csv'),delimiter=',')
for row in reldata:
q = Release(number = row[0],
notes= row[1],
changes = row[2],
metamodules = Metamodule.objects.filter(name = row[3]))
try:
q.save()
except:
# if the're a problem anywhere, you wanna know about it
print "there was a problem with line"
Error:
Traceback (most recent call last):
File "<console>", line 5, in <module>
File "/usr/local/lib/python2.7/site-packages/django/db/models/base.py", line 416, in __init__
raise TypeError("'%s' is an invalid keyword argument for this function" % list(kwargs)[0])
TypeError: 'metamodules' is an invalid keyword argument for this function
As the field is ManyToManyField i used objects.fileter to get multiple records. But it is returning error.Please help me to fix this issue
models.py:
class Metamodule(models.Model):
name = models.CharField(max_length=50)
version = models.IntegerField(default=0)
modulename = models.ForeignKey(Module)
createdate = models.DateField(auto_now=True, null=True)
createdby = models.CharField(max_length=50)
def __unicode__(self):
return unicode(self.name)
class Release(models.Model):
number = models.IntegerField(default=0)
notes = models.CharField(max_length=50)
changes = models.CharField(max_length=50)
metamodules = models.ManyToManyField(Metamodule)
def __unicode__(self):
return unicode(self.number)
You can't create your Release object like that. You cannot create m2m relations from unsaved objects. See here
Try something like this :
for row in reldata:
q = Release(number=row[0], notes=row[1], changes=row[2])
# You have to save the object before adding the m2m relations
q.save()
metamodules = Metamodule.objects.filter(name=row[3])
for metamodule in metamodules:
q.metamodules.add(metamodule)
There is probably a better way to do the for loop but this is what you want to achieve.

Categories