Django Tastypie POST request for OneToOne relation - django

I have recently started django-tastypie and so far loving the framework. With said that, I am getting below issue on POST for OneToOne relation to model and spent good amount of time but couldn't figured whats missing. Here is the Model and Resource code -
Model
class Question(TimeStampedModel):
question_title = models.CharField("question title", max_length=100)
question_desc = models.TextField("question description", max_length=1000)
.......
Second model is having OneToOne relation with Question -
class QuestionAnswer(TimeStampedModel):
question = models.OneToOneField(Question)
.....
Resource
QuestionAnswerResource -
class QuestionAnswerResource(ModelResource):
question = fields.ForeignKey('myapp.api.QuestionResource', 'question')
class Meta:
queryset = QuestionAnswer.objects.all()
resource_name='questionanswer'
QuestionResource -
class QuestionResource(ModelResource):
questionanswer = fields.OneToOneField('myapp.api.QuestionAnswerResource', 'questionanswer', full=True)
class Meta:
queryset = Question.objects.all()
resource_name = 'question'
With the above setup, I get correct response on GET with Question instance along with its answer attribute. However when I try to POST data to save question/answer on this one it fails with below error -
"{"error_message": "", "traceback": "Traceback (most recent call
last):\n\n File
\"/Library/Python/2.7/site-packages/tastypie/resources.py\", line 192,
...................................................................................... line 636, in hydrate\n
value = super(ToOneField, self).hydrate(bundle)\n\n File
\"/Library/Python/2.7/site-packages/tastypie/fields.py\", line 154, in
hydrate\n elif self.attribute and getattr(bundle.obj,
self.attribute, None):\n\n File
\"/Library/Python/2.7/site-packages/Django-1.4.1-py2.7.egg/django/db/models/fields/related.py\", line 343, in get\n raise
self.field.rel.to.DoesNotExist\n\nDoesNotExist\n"}"
Can someone point out what I am missing?

I think that this:
questionanswer = fields.OneToOneField('myapp.api.QuestionAnswerResource', 'questionanswer', full=True)
causes problem. There is no questionanswer field in your Question model and Django expects that.
To test, you may want to see whether you can create instances in the shell. Tastypie docs say the following about the ToOneField:
This subclass requires Django’s ORM layer to work properly.
and I think this might not be the case here.
To provide answers for a question you may want to set related_name, like so:
question = fields.ForeignKey('myapp.api.QuestionResource', 'question', related_name = 'questionanswer')

Related

Django Object is Not Serializable CommandError using Dumpdata with Natural Keys

I am trying to use 'natural keys' for serialization (docs) in a "manage.py dumpdata" command:
python manage.py dumpdata --natural-primary --natural-foreign --indent 4 --format json --verbosity 1 > tests\test_fixtures\test_db2.json
and I am getting the following error when I use --natural-foreign on other apps that use the Project or Task model (which they all must by design):
CommandError: Unable to serialize database: Object of type Project is not JSON serializable
Exception ignored in: <generator object cursor_iter at 0x000001EF62481B48>
Traceback (most recent call last):
File "C:\Users\Andrew\anaconda3\envs\Acejet_development\lib\site-packages\django\db\models\sql\compiler.py", line 1586, in cursor_iter
cursor.close()
sqlite3.ProgrammingError: Cannot operate on a closed database.
If I just dumpdata from this, the 'projects' app, it works, but other apps are built with entities related to Project or Task and there the --natural-foreign option fails.
The problem occurs when a model (say Question) calls for a natural_key from Task, which includes a for a natural_key from Project.
If I use the Pycharm Python Console to access querysets of Projects or Tasks ('q' here), this works:
serializers.serialize('json', q, indent=2, use_natural_foreign_keys=True, use_natural_primary_keys=True)
But if 'w' is a list of Question objects from another app that have a Task foreign key I get this error:
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "C:\Users\Andrew\anaconda3\envs\Acejet_development\lib\site-packages\django\core\serializers\__init__.py", line 128, in serialize
s.serialize(queryset, **options)
File "C:\Users\Andrew\anaconda3\envs\Acejet_development\lib\site-packages\django\core\serializers\base.py", line 115, in serialize
self.end_object(obj)
File "C:\Users\Andrew\anaconda3\envs\Acejet_development\lib\site-packages\django\core\serializers\json.py", line 53, in end_object
json.dump(self.get_dump_object(obj), self.stream, **self.json_kwargs)
File "C:\Users\Andrew\anaconda3\envs\Acejet_development\lib\json\__init__.py", line 179, in dump
for chunk in iterable:
File "C:\Users\Andrew\anaconda3\envs\Acejet_development\lib\json\encoder.py", line 431, in _iterencode
yield from _iterencode_dict(o, _current_indent_level)
File "C:\Users\Andrew\anaconda3\envs\Acejet_development\lib\json\encoder.py", line 405, in _iterencode_dict
yield from chunks
File "C:\Users\Andrew\anaconda3\envs\Acejet_development\lib\json\encoder.py", line 405, in _iterencode_dict
yield from chunks
File "C:\Users\Andrew\anaconda3\envs\Acejet_development\lib\json\encoder.py", line 325, in _iterencode_list
yield from chunks
File "C:\Users\Andrew\anaconda3\envs\Acejet_development\lib\json\encoder.py", line 438, in _iterencode
o = _default(o)
File "C:\Users\Andrew\anaconda3\envs\Acejet_development\lib\site-packages\django\core\serializers\json.py", line 104, in default
return super().default(o)
File "C:\Users\Andrew\anaconda3\envs\Acejet_development\lib\json\encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type Project is not JSON serializable
The models are:
# projects.models.py
class BaseModelWithHistory(models.Model):
history = HistoricalRecords(inherit=True)
natural_key_fields = ('id',) # default
class Meta:
abstract = True
def natural_key(self):
fieldlist = [getattr(self, fieldname) for fieldname in self.natural_key_fields]
return tuple(fieldlist)
# natural_key.dependencies = ['projects.Project', 'projects.Task'] # serialize these first.
class Project(BaseModelWithHistory):
"""
'Projects' group Tasks.
"""
project_name = models.CharField(max_length=200, default="development_project")
project_short_description = models.CharField(
max_length=500,
default="This is the default text.")
target_group = models.ManyToManyField(Group, blank=True)
objects = ProjectDiscreteManager()
natural_key_fields = ('project_name',)
class Task(BaseModelWithHistory):
number = models.PositiveIntegerField(default=0)
name = models.CharField(max_length=200, default='new task')
project = models.ForeignKey(Project, on_delete=models.CASCADE)
target_group = models.ManyToManyField(Group, blank=True)
app_label = models.CharField(max_length=80, choices=app_choices(), null=True, blank=True)
objects = discrete_manager_factory('project')
natural_key_fields = ('project', 'name', 'number')
Things that I wouldn't expect to cause this problem but I could certainly be wrong:
The ProjectDiscreteManager and others created by discrete_manager_factory() behave exactly as the default manager (models.Manager()), unless its called with a request from an identified user, in which case it adds a filter to see if that user is in the Group.
All models define the natural_keys tuple because the parent class defines it as ('id',); most models overwrite this with more representative fields.
With the natural_key.dependencies list set for all models to prioritize Project and then Task, I get a 'can't resolve dependencies' error for every other model. I think this ticket relates, but am not sure how to track down whether this fix is already in the Django 3.0.6 I'm using and I should just straighten up & fly right, or if my Han Solo 'this should wooork' will one day soon be rewarded. [Update: I worked out it is coming in Django 3.1.1, but I'm not sure that it is going to fix the "can't resolve dependencies" error I've created for myself.]

Get Data from Choice field in Django rest framework

I have searched and tried out all solutions provided for this question in similar problem but they're not working for me. I am getting the following error when trying to get data from an endpoint
Got AttributeError when attempting to get a value for field
hotel_type on serializer HotelDetailSerializer. The serializer
field might be named incorrectly and not match any attribute or key on
the Hotels instance. Original exception text was: 'Hotels' object
has no attribute 'get_hotel_Type_display'.
This is my model field truncated for clarity
class Hotels(models.Model):
HOTEL_TYPE = (
('hotel', "Hotel"),
('apartment', "Apartment"),
('villa', "Villa"),
hotel_Type = models.CharField(
max_length=20,
choices=HOTEL_TYPE,
default='hotel', null=True, blank=True
#property
def hotel_type(self):
return self.get_hotel_Type_display()
This is my serializer class also truncated for clarity
class HotelDetailSerializer(serializers.ModelSerializer):
hotel_type = serializers.Field(source='hotel_type.hotel_Type')
class Meta:
model = Hotels
fields = ("hotel_type" )
This is the apiview
class HotelDetailAPIView(RetrieveAPIView):
"""Display details of a single hotel"""
queryset = Hotels.objects.all()
serializer_class = HotelDetailSerializer
permission_classes = ()
lookup_field = 'slug'
Could anyone kindly assist me figure out why this is not working? Thanks
EDIT
I am editing this question to add more context. I have been doing some debugging in Django shell. This is what i am getting
>>> from hotels.models import Hotels
>>> h = Hotels.objects.all()
>>> for n in h:
... print (n.hotel_Type)
...
(<django.db.models.fields.CharField>,)
>>> for n in h:
... print (n.get_hotel_Type_display())
...
Traceback (most recent call last):
File "<console>", line 2, in <module>
AttributeError: 'Hotels' object has no attribute 'get_hotel_Type_display'
I am following Django's get_FOO_display() tutorial and i still cannot get this work. I am not seeing anything wrong with my Hotels model. Could this be a bug in Django? Kindly assist
This really has eaten me up. I finally found the issue was a misplaced comma on my model field as shown below
class Hotels(models.Model):
HOTEL_TYPE = (
('hotel', "Hotel"),
('apartment', "Apartment"),
('villa', "Villa"),
hotel_Type = models.CharField(
max_length=20,
choices=HOTEL_TYPE,
default='hotel', null=True, blank=True
),# <--------------This comma was the source of my problems
#property
def hotel_type(self):
return self.get_hotel_Type_display()

Django ManyToMany relationship having through parameter [duplicate]

I have a many-to-many relationship with a through table like so:
class Chapter(models.Model):
name = models.CharField(max_length=255,)
slides = models.ManyToManyField('Slide', blank=True, related_name='chapters', through='SlideOrder')
# ...
class Slide(models.Model):
title = models.CharField(max_length=255,)
# ...
class SlideOrder(models.Model):
chapter = models.ForeignKey(Chapter)
slide = models.ForeignKey(Slide)
number = models.PositiveIntegerField()
I am able to get the slides for a chapter in order like so:
chapter = Chapter.objects.get(pk=1)
chapter_slides = chapter.slides.order_by('slideorder')
However, when working on an individual slide instance I am unable to access the slide order:
slide = Slide.objects.get(pk=1)
If I do the following on my slide instance I can see all possible fields:
print slide._meta.get_all_field_names()
['title', u'chapters', 'slideorder', u'id']
However trying to access the slideorder field gives me the following:
slide.slideorder
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Slide' object has no attribute 'slideorder'
I am able to access all attributes listed except slideorder. How can I access a slide's order?
You can either filter on the SlideOrder model directly
slide = Slide.objects.get(pk=1)
slide_orders = SlideOrder.objects.filter(slide=slide)
for slide_order in slide_orders:
print slide_order.number
or follow the foreign key backwards:
slide = Slide.objects.get(pk=1)
slide_orders = slide.slideorder_set.all()
for slide_order in slide_orders:
print slide_order.number
See the docs on extra fields on many-to-many relationships for more info.
You can use slide.slideorder_set as documented in the django docs

Django unique_together with nullable ForeignKey

I'm using Django 1.8.4 in my dev machine using Sqlite and I have these models:
class ModelA(Model):
field_a = CharField(verbose_name='a', max_length=20)
field_b = CharField(verbose_name='b', max_length=20)
class Meta:
unique_together = ('field_a', 'field_b',)
class ModelB(Model):
field_c = CharField(verbose_name='c', max_length=20)
field_d = ForeignKey(ModelA, verbose_name='d', null=True, blank=True)
class Meta:
unique_together = ('field_c', 'field_d',)
I've run proper migration and registered them in the Django Admin. So, using the Admin I've done this tests:
I'm able to create ModelA records and Django prohibits me from creating duplicate records - as expected!
I'm not able to create identical ModelB records when field_b is not empty
But, I'm able to create identical ModelB records, when using field_d as empty
My question is: How do I apply unique_together for nullable ForeignKey?
The most recent answer I found for this problem has 5 year... I do think Django have evolved and the issue may not be the same.
Django 2.2 added a new constraints API which makes addressing this case much easier within the database.
You will need two constraints:
The existing tuple constraint; and
The remaining keys minus the nullable key, with a condition
If you have multiple nullable fields, I guess you will need to handle the permutations.
Here's an example with a thruple of fields that must be all unique, where only one NULL is permitted:
from django.db import models
from django.db.models import Q
from django.db.models.constraints import UniqueConstraint
class Badger(models.Model):
required = models.ForeignKey(Required, ...)
optional = models.ForeignKey(Optional, null=True, ...)
key = models.CharField(db_index=True, ...)
class Meta:
constraints = [
UniqueConstraint(fields=['required', 'optional', 'key'],
name='unique_with_optional'),
UniqueConstraint(fields=['required', 'key'],
condition=Q(optional=None),
name='unique_without_optional'),
]
UPDATE: previous version of my answer was functional but had bad design, this one takes in account some of the comments and other answers.
In SQL NULL does not equal NULL. This means if you have two objects where field_d == None and field_c == "somestring" they are not equal, so you can create both.
You can override Model.clean to add your check:
class ModelB(Model):
#...
def validate_unique(self, exclude=None):
if ModelB.objects.exclude(id=self.id).filter(field_c=self.field_c, \
field_d__isnull=True).exists():
raise ValidationError("Duplicate ModelB")
super(ModelB, self).validate_unique(exclude)
If used outside of forms you have to call full_clean or validate_unique.
Take care to handle the race condition though.
#ivan, I don't think that there's a simple way for django to manage this situation. You need to think of all creation and update operations that don't always come from a form. Also, you should think of race conditions...
And because you don't force this logic on DB level, it's possible that there actually will be doubled records and you should check it while querying results.
And about your solution, it can be good for form, but I don't expect that save method can raise ValidationError.
If it's possible then it's better to delegate this logic to DB. In this particular case, you can use two partial indexes. There's a similar question on StackOverflow - Create unique constraint with null columns
So you can create Django migration, that adds two partial indexes to your DB
Example:
# Assume that app name is just `example`
CREATE_TWO_PARTIAL_INDEX = """
CREATE UNIQUE INDEX model_b_2col_uni_idx ON example_model_b (field_c, field_d)
WHERE field_d IS NOT NULL;
CREATE UNIQUE INDEX model_b_1col_uni_idx ON example_model_b (field_c)
WHERE field_d IS NULL;
"""
DROP_TWO_PARTIAL_INDEX = """
DROP INDEX model_b_2col_uni_idx;
DROP INDEX model_b_1col_uni_idx;
"""
class Migration(migrations.Migration):
dependencies = [
('example', 'PREVIOUS MIGRATION NAME'),
]
operations = [
migrations.RunSQL(CREATE_TWO_PARTIAL_INDEX, DROP_TWO_PARTIAL_INDEX)
]
Add a clean method to your model - see below:
def clean(self):
if Variants.objects.filter("""Your filter """).exclude(pk=self.pk).exists():
raise ValidationError("This variation is duplicated.")
I think this is more clear way to do that for Django 1.2+
In forms it will be raised as non_field_error with no 500 error, in other cases, like DRF you have to check this case manual, because it will be 500 error.
But it will always check for unique_together!
class BaseModelExt(models.Model):
is_cleaned = False
def clean(self):
for field_tuple in self._meta.unique_together[:]:
unique_filter = {}
unique_fields = []
null_found = False
for field_name in field_tuple:
field_value = getattr(self, field_name)
if getattr(self, field_name) is None:
unique_filter['%s__isnull' % field_name] = True
null_found = True
else:
unique_filter['%s' % field_name] = field_value
unique_fields.append(field_name)
if null_found:
unique_queryset = self.__class__.objects.filter(**unique_filter)
if self.pk:
unique_queryset = unique_queryset.exclude(pk=self.pk)
if unique_queryset.exists():
msg = self.unique_error_message(self.__class__, tuple(unique_fields))
raise ValidationError(msg)
self.is_cleaned = True
def save(self, *args, **kwargs):
if not self.is_cleaned:
self.clean()
super().save(*args, **kwargs)
One possible workaround not mentioned yet is to create a dummy ModelA object to serve as your NULL value. Then you can rely on the database to enforce the uniqueness constraint.

Django get method is yielding an error

I'm following a Django book (Django 1.0 Web Site Development). I'm finding that the book, although straight forward and easy to read, leaves out small details. However, this error that I'm getting, I have not been able to find a solution online. Thanks for any help.
Below, I added the Tag class to my models.py file.
from django.db import models
from django.contrib.auth.models import User
class Link(models.Model):
url = models.URLField(unique=True)
class Bookmark(models.Model):
title = models.CharField(max_length=200)
user = models.ForeignKey(User)
link = models.ForeignKey(Link)
class Tag(models.Model):
name = models.CharField(max_length=64, unique=True)
bookmarks = models.ManyToManyField(Bookmark)
Then I attempt to run the following code at the python shell:
from bookmarks.models.import *
bookmark = Bookmark.objects.get(id=1)
As a result, I get the following error:
Traceback (most recent call last):
File "(console)", line 1, in (module)
File "c:\Python27\lib\site\-packages\django\db\models\manager.py", line 132, in get
return self.get_query_set().get(*args, **kwargs)
File "c:\Python27\lib\site-packages\django\db\models\query.py", line 349, in get
% self.model._meta.object_name)
DoesNotExist: Bookmark matching query does not exist.
The error means just what it says. DoesNotExist is raised by QuerySet.get() if there is no object in the database that would match the conditions given to the QuerySet. In this case it means there is no Bookmark object in the database with an ID equal to 1.
Did you add any data in the Bookmark table yet? DoesNotExist is raised by get if there is no record corresponding to your query. i.e. if there is no record corresponding to id=1.