ManyToManyField with model_to_dict(self) - django

Not sure if the title is the correct one, sorry for the inconvenience.
I'm having a problem on sending a ManyToManyField from a model to a dictionary using the model_to_dict() Below is my code
models.py
from django.db import models
from django.forms import model_to_dict
from app_1.models import *
class Stuff(models.Model):
thing_1 = models.CharField(max_length=100, null=True, blank=True)
thing_2 = models.ManyToManyField(OtherStuff, blank=True, related_name="thing")
def toJSON(self):
item = model_to_dict(self)
item['thing'] = self.thing.toJSON()
return item
When I run a query and load my Stuff model, I get the following error:
from app_2.models import *
s = Stuff.objects.get(pk=1)
# here is where I send my model to a dictionary
s.toJSON()
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "P:\test\app\app_2\stuff\models.py", line 10, in toJSON
return item
AttributeError: 'ManyRelatedManager' object has no attribute 'toJSON'
I've come across multiple ways of sending a ManyToManyField to a dictionary, however, none of them use the model_to_dict(). I'd like to use this method due to it's simplicity of usage.

With this approach, you'll need to use this syntax:
item['thing'] = [t.toJSON() for t in self.thing_2.all()]
And also, implement the toJSON method for the OtherStuff model.
Or you can use model_to_dict for OtherStuff as well:
def toJSON(self):
item = model_to_dict(self)
item['thing'] = [model_to_dict(t) for t in self.thing_2.all()]
return item

Related

Querying related fields from Django Tables with One to Many Relationship

I am just exploring how to get around with Django, and i created two models in my Django app.
from django.db import models
#first model
class Person(models.Model):
name = models.CharField(max_length=40)
email = models.CharField(max_length=100)
title = models.CharField(max_length=100)
image = models.CharField(max_length=200)
def __str__(self):
return self.name
#second model
class Skill(models.Model):
person = models.ForeignKey(Person)
skill = models.CharField(max_length=60)
years = models.CharField(max_length=40)
def __str__(self):
return self.skill, self.person
The first model is Person and the second model is Skill. Now how the relation goes is that each Person will have many skills.
Now I can update the database with the data, the admin section of the site also works fine.
On the Django Shell, I try to run the command:
Skill.object.all()
and what i get is the following error:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "C:\Program Files (x86)\Python36-32\lib\site-packages\django\db\models\query.py", line 235, in __repr__
return '<QuerySet %r>' % data
File "C:\Program Files (x86)\Python36-32\lib\site-packages\django\db\models\base.py", line 572, in __repr__
u = six.text_type(self)
TypeError: __str__ returned non-string (type tuple)
or if i try the command:
Skill.objects.get(pk=1)
i get:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "C:\Program Files (x86)\Python36-32\lib\site-packages\django\db\models\base.py", line 572, in __repr__
u = six.text_type(self)
TypeError: __str__ returned non-string (type tuple)
However if i run a command such as :
Skill.objects.get(skill='Photoshop').person.name
I get the name of the person who has the skill "Photoshop."
I am trying to understand what I am doing wrong here; maybe I am not supposed to query a table with the foreign key this way? Or maybe I am doing something wrong.
Well, finally what I like to query is, I want to find all the skills of a Person with a given name or primary key.
__str__ should return a str. So Change something like this
return self.skill, self.person
to
return "%s-%s" %(self.skill, self.person.name)
Your __str__ method returns a tuple (self.skill, self.person), it must return those object's str representation. In order to achieve that, change:
return self.skill, self.person
to
return "{}, {}".format(self.skill, self.person)

Get and set ForeignKey directly in Django

I'm trying to work with Django model created from a mysql database which has composite foreign keys.
My models.py goes like this.
class Make(models.Model):
idmake = models.IntegerField(primary_key=True)
make = models.CharField(max_length=20L, unique=True, blank=True)
def __unicode__(self):
return self.make
class Meta:
db_table = 'make'
class Models(models.Model):
idmodels = models.IntegerField(primary_key=True, unique=True)
make = models.ForeignKey(Make, db_column='make', to_field='make')
model = models.CharField(max_length=45L, unique=True)
resource_type = models.CharField(max_length=7L)
def __unicode__(self):
return self.model
class Meta:
db_table = 'models'
class Systems(models.Model):
idsystems = models.IntegerField(primary_key=True, unique=True)
make = models.ForeignKey(Models, null=True, db_column='make', blank=True, related_name='system_make')
model = models.ForeignKey(Models, null=True, db_column='model', to_field = 'model', blank=True, related_name='system_model')
serial_num = models.CharField(max_length=45L, blank=True)
service_tag = models.CharField(max_length=45L, blank=True)
mac = models.CharField(max_length=45L, unique=True)
Now when I try to access the make field of Systems I get a ValueError.
>>> s = Systems.objects.get(pk=1)
>>> s.model
<Models: model11>
>>> s.model.make
<Make: make1>
>>> s.make
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/lib/python2.7/site-packages/django/db/models/fields/related.py", line 384, in __get__
rel_obj = qs.get(**params)
File "/usr/lib/python2.7/site-packages/django/db/models/query.py", line 395, in get
clone = self.filter(*args, **kwargs)
File "/usr/lib/python2.7/site-packages/django/db/models/query.py", line 669, in filter
return self._filter_or_exclude(False, *args, **kwargs)
File "/usr/lib/python2.7/site-packages/django/db/models/query.py", line 687, in _filter_or_exclude
clone.query.add_q(Q(*args, **kwargs))
File "/usr/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1271, in add_q
can_reuse=used_aliases, force_having=force_having)
File "/usr/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1202, in add_filter
connector)
File "/usr/lib/python2.7/site-packages/django/db/models/sql/where.py", line 71, in add
value = obj.prepare(lookup_type, value)
File "/usr/lib/python2.7/site-packages/django/db/models/sql/where.py", line 339, in prepare
return self.field.get_prep_lookup(lookup_type, value)
File "/usr/lib/python2.7/site-packages/django/db/models/fields/__init__.py", line 1003, in get_prep_lookup
return super(IntegerField, self).get_prep_lookup(lookup_type, value)
File "/usr/lib/python2.7/site-packages/django/db/models/fields/__init__.py", line 322, in get_prep_lookup
return self.get_prep_value(value)
File "/usr/lib/python2.7/site-packages/django/db/models/fields/__init__.py", line 997, in get_prep_value
return int(value)
ValueError: invalid literal for int() with base 10: 'make1'
I'm not allowed to change the tables and relations in the database. I'm very new to Django and I'm unable to figure out what is the correct fix for this issue. Basically I would like to be able to get and set the make field of the Systems model directly. Can someone guide me on how I should go about doing this? My initial thoughts were that I would have to create a custom ForeignKey field.
I suspect your codes don't match your DB schema. In this line:
make = models.ForeignKey(Models, null=True, db_column='make', blank=True, related_name='system_make')
Models is supposed to be Make, isn't it? And your Systems.model has to_field = 'model', did you miss to_field='make' for Systems.make? I suggest you to drop the whole DB, run syncdb and create test data again. Then see if the error still happen.
Some more tips for your code:
as your defined to_field = 'model' and to_field='make', you'd better to consider add db_index=True for make and model fields. Otherwise query performance may be bad when your dataset is large
if you're going to set Make.make and Models.model to be unique and indexed, they seems to be qualified as primary key. Are the idmake and idmodels really necessary in your case?
primary_key=True guarantees unique. unique=True is redundant
Django conversions use singular form for model definition. I.e., use Model System, rather than Models Systems. Also, usually we use id, rather than idmake, idmodels. Those are just conversions, up to you
I discovered the answer, rather by accident. Would like to share for anyone facing similar problem.
To access make directly from Systems
>>> s = Systems.objects.get(pk=1)
>>> s.model
<Models: model11>
>>> s.model.make
<Make: make1>
>>> s.make_id
u'make1'
To set make directly from a Systems object
>>> s.make_id = Models.objects.get(model='model21').make.make
>>> s.save()
>>> s.make_id
u'make2'
Be warned. This will not work if the get method of Models returns multiple or no model objects. For example if my Models table was as:
then
>>>> Models.objects.get(model='unknown').make.make
Traceback (most recent call last):
....
MultipleObjectsReturned: get() returned more than one Models -- it returned 2!
>>> Models.objects.get(model='not known').make.make
Traceback (most recent call last):
....
DoesNotExist: Models matching query does not exist.
I fell the programmer needs to be cautious about these things.
EDIT
From comments by #ZZY to this answer and to his own answer
class Models(models.Model):
idmodels = models.IntegerField(primary_key=True, unique=True)
make = models.ForeignKey(Make, db_column='make', to_field='make')
model = models.CharField(max_length=45L, unique=True)
resource_type = models.CharField(max_length=7L)
def __unicode__(self):
return self.model
class Meta:
db_table = 'models'
unique_together = ("make", "model")
And also since model field in Models is unique, the scenario described by me should not occur in a correct table

Select field for all models within a Django app

I am pretty new to Django.
I wanted to create a form for some user information. Depending on type of user informations, the fields should change... For example the users private address needs the fields name, street, zip and city. But if he wants something send to the company, there might be more fields like department or company name.
I want to implement something like this and create for each kind of input an extra model compact in a separate app.
Is there a way to get a select field with a list of all available models in this app.
Edit
Since I have some further problems, I add an example here
file: experiment/models.py
from django.db import models
from django.apps import apps
class BasicExperiment(models.Model):
date_created = models.DateTimeField(editable=False)
date_modified = models.DateTimeField(blank=True)
label_app = apps.get_app('labels')
label_types = apps.get_models(label_app)
file: labels/models.py
from django.db import models
class SILAC(models.Model):
lys0 = models.BooleanField('Lys-0', default=True)
lys4 = models.BooleanField('Lys-4', default=None)
lys8 = models.BooleanField('Lys-8', default=None)
arg0 = models.BooleanField('Arg-0', default=True)
arg6 = models.BooleanField('Arg-6', default=None)
arg10 = models.BooleanField('Arg-10', default=None)
class Meta:
verbose_name = 'SILAC Labeling'
In the shell it works as expected:
>>> from django.apps import apps
>>> app = apps.get_app('labels')
>>> for model in apps.get_models(app):
... model._meta.verbose_name
...
'SILAC Labeling'
Within my models.py I get the following error:
...
File "/Users/madejung/Documents/django_dev/cfproteomics/experiments/models.py", line 5, in <module>
class BasicExperiment(models.Model):
File "/Users/madejung/Documents/django_dev/cfproteomics/experiments/models.py", line 10, in BasicExperiment
label_app = apps.get_app('labels')
File "/Library/Python/2.7/site-packages/django/apps/registry.py", line 370, in get_app
"App '%s' doesn't have a models module." % app_label)
django.core.exceptions.ImproperlyConfigured: App 'labels' doesn't have a models module.
You could try this:
from django.db.models import get_app, get_models
app = get_app('my_application_name')
for model in get_models(app):
# do something with the model
Here there is more information Django get list of models in application

Django: Querying many-to-one model instance fields

I've been playing around in the Django tutorial and am trying to get the field of a model instance associated (via foreign key) to another model instance. Here is what I mean:
Model file:
from django.db import models
class Usercar(models.Model):
make = models.CharField(max_length=200)
model = models.CharField(max_length=200)
year = models.DateTimeField('year of car')
def __unicode__(self):
return self.make
class Suggestcar(models.Model):
usercar = models.ForeignKey(Usercar)
make = models.CharField(max_length=200)
model = models.CharField(max_length=200)
year = models.DateTimeField('year of car')
def __unicode__(self):
return self.make
In the shell, this is what I get when I try to get the make of a suggestcar related to a usercar:
>>from django.utils import timezone
>>mycar1 = Usercar(make="Ford",model="Escort",year=timezone.now())
>>mycar1.save()
>>mycar1.suggestcar_set.create(make="Ford", model="Taurus", year=timezone.now())
>>mycar1.suggestcar_set.filter(id=1).make
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site- packages/django/core/management/commands/shell.pyc in <module>()
----> 1 mycar1.suggestcar_set.filter(id=1).make
AttributeError: 'QuerySet' object has no attribute 'make'
How can I get the make of this mycar1.suggestcar_set.filter(id=1)? Thanks!
filter returns a queryset, if you want to access the objects in it, you need to iterate over the queryset or get the first result from it.
mycar1.suggestcar_set.filter(id=1)[0].make
If you want to return just 1 object, you should really use get
mycar1.suggestcar_set.get(id=1).make

Django ManyToManyField error object has no attribute 'location_set'

This seems like a really trivial question, but it is killing me.
models.py
class Location(models.Model):
place = models.CharField("Location", max_length=30)
[...]
class Person(models.Model):
first = models.CharField("First Name", max_length=50)
[...]
location = models.ManyToManyField('Location')
From the shell:
>>> from mysite.myapp.models import *
>>> p = Person.objects.get(id=1)
>>> p
<Person: bob >
>>> l = Location(place='123 Main')
>>> p.location_set.add(l)
>>> p.save()
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'location_set'
I'm really not seeing what I'm missing.
Shouldn't you be using p.location.add()? location_set or <modelname>_set is the default name for the reverse lookup for that model.
location_set would be the default name for a backward relation, however since you've defined the ManyToManyField on the Person model, you can access the related manager via the field name:
p.location.add(l)
With this in mind, it makes more sense to name the ManyToManyField as a pluralised noun, e.g.
class Person(models.Model):
first = models.CharField("First Name", max_length=50)
[...]
locations = models.ManyToManyField('Location')
Also, from memory, when you try to add model instances to a many-to-many relationship, the instance must be saved prior to adding.