Skip to content

Commit

Permalink
Merge pull request #187 from dpaluch-rp/fix/176
Browse files Browse the repository at this point in the history
#176 Fix: Sideloaded object not have all included fields
  • Loading branch information
aleontiev authored Jul 17, 2017
2 parents 8b00d4a + b7f2f5a commit c0e17f2
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 5 deletions.
34 changes: 34 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
FROM ubuntu:xenial
ENV DEBIAN_FRONTEND noninteractive
ENV TERM=xterm


RUN apt-get -y --force-yes update
RUN apt-get -y --force-yes install locales


# Set the locale
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

# Upgrade packages
RUN apt-get -y --force-yes upgrade
RUN apt-get -y --force-yes install software-properties-common curl git wget unzip nano build-essential autoconf libxml2-dev libssl-dev libbz2-dev libcurl3-dev libjpeg-dev libpng-dev libfreetype6-dev libgmp3-dev libc-client-dev libldap2-dev libmcrypt-dev libmhash-dev freetds-dev libz-dev ncurses-dev libpcre3-dev libsqlite-dev libaspell-dev libreadline6-dev librecode-dev libsnmp-dev libtidy-dev libxslt-dev
RUN apt-get -y --force-yes install ruby-dev debhelper python3-dev devscripts libxml2-dev

RUN apt-get -y --force-yes install python3-pip python3-setuptools libpython3-dev
RUN apt-get -y --force-yes install python-pip python-setuptools libpython-dev
RUN apt-get install locales
RUN add-apt-repository "deb http://repo.aptly.info/ squeeze main" -y
RUN apt-key adv --keyserver keys.gnupg.net --recv-keys E083A3782A194991
RUN apt-get update
RUN apt-get -yq --force-yes install dh-virtualenv goaccess aptly

RUN apt-get install postgresql libpq-dev postgresql-client postgresql-client-common -y
RUN apt-get autoclean

ADD . /home/

WORKDIR /home/
6 changes: 6 additions & 0 deletions dynamic_rest/processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ def process(self, obj, parent=None, parent_key=None, depth=0):
# move the object into a new top-level bucket
# and mark it as seen
self.data[name].append(obj)
else:
# obj sideloaded, but maybe with other fields
for o in self.data[name]:
if o.instance.pk == pk:
o.update(obj)
break

# replace the object with a reference
if parent is not None and parent_key is not None:
Expand Down
45 changes: 45 additions & 0 deletions tests/migrations/0005_auto_20170712_0759.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.3 on 2017-07-12 07:59
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('tests', '0004_user_is_dead'),
]

operations = [
migrations.CreateModel(
name='Car',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), # noqa
('name', models.CharField(max_length=60)),
],
),
migrations.CreateModel(
name='Country',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), # noqa
('name', models.CharField(max_length=60)),
('short_name', models.CharField(max_length=30)),
],
),
migrations.CreateModel(
name='Part',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), # noqa
('name', models.CharField(max_length=60)),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tests.Car')), # noqa
('country', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tests.Country')), # noqa
],
),
migrations.AddField(
model_name='car',
name='country',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tests.Country'), # noqa
),
]
16 changes: 16 additions & 0 deletions tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,19 @@ class C(models.Model):

class D(models.Model):
name = models.TextField(blank=True)


class Country(models.Model):
name = models.CharField(max_length=60)
short_name = models.CharField(max_length=30)


class Car(models.Model):
name = models.CharField(max_length=60)
country = models.ForeignKey(Country)


class Part(models.Model):
car = models.ForeignKey(Car)
name = models.CharField(max_length=60)
country = models.ForeignKey(Country)
32 changes: 31 additions & 1 deletion tests/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@
DynamicModelSerializer
)
from tests.models import (
Car,
Cat,
Country,
Dog,
Group,
Horse,
Location,
Part,
Permission,
Profile,
User,
Zebra
Zebra,
)


Expand Down Expand Up @@ -280,3 +283,30 @@ class Meta:
'name',
'origin',
)


class CountrySerializer(DynamicModelSerializer):

class Meta:
model = Country
fields = ('id', 'name', 'short_name')
deferred_fields = ('name', 'short_name')


class PartSerializer(DynamicModelSerializer):
country = DynamicRelationField('CountrySerializer')

class Meta:
model = Part
fields = ('id', 'name', 'country')
deferred_fields = ('name', 'country')


class CarSerializer(DynamicModelSerializer):
country = DynamicRelationField('CountrySerializer')
parts = DynamicRelationField('PartSerializer', many=True, source='part_set') # noqa

class Meta:
model = Car
fields = ('id', 'name', 'country', 'parts')
deferred_fields = ('name', 'country', 'parts')
56 changes: 53 additions & 3 deletions tests/setup.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
from collections import namedtuple

from tests.models import (
Car,
Cat,
Country,
Dog,
Event,
Group,
Horse,
Location,
Part,
Permission,
User,
Zebra
)
)


def create_fixture():
Expand All @@ -20,16 +23,20 @@ def create_fixture():
# 2 of the users share the same location
# 2 of the users have their own locations
# Create 4 dogs.
# Create 2 Country
# Create 1 Car has 2 Parts each from different Country

types = [
'users', 'groups', 'locations', 'permissions',
'events', 'cats', 'dogs', 'horses', 'zebras'
'events', 'cats', 'dogs', 'horses', 'zebras',
'cars', 'countries', 'parts',
]
Fixture = namedtuple('Fixture', types)

fixture = Fixture(
users=[], groups=[], locations=[], permissions=[],
events=[], cats=[], dogs=[], horses=[], zebras=[]
events=[], cats=[], dogs=[], horses=[], zebras=[],
cars=[], countries=[], parts=[]
)

for i in range(0, 4):
Expand Down Expand Up @@ -185,4 +192,47 @@ def create_fixture():
fixture.groups[0].permissions.add(fixture.permissions[0])
fixture.groups[1].permissions.add(fixture.permissions[1])

countries = [{
'id': 1,
'name': 'United States',
'short_name': 'US',
}, {
'id': 2,
'name': 'China',
'short_name': 'CN',
}]

cars = [{
'id': 1,
'name': 'Porshe',
'country': 1
}]

parts = [{
'car': 1,
'name': 'wheel',
'country': 1
}, {
'car': 1,
'name': 'tire',
'country': 2
}]

for country in countries:
fixture.countries.append(Country.objects.create(**country))

for car in cars:
fixture.cars.append(Car.objects.create(
id=car.get('id'),
name=car.get('name'),
country_id=car.get('country')
))

for part in parts:
fixture.parts.append(Part.objects.create(
car_id=part.get('car'),
name=part.get('name'),
country_id=part.get('country')
))

return fixture
19 changes: 19 additions & 0 deletions tests/test_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,25 @@ def test_sideload(self):
self.assertTrue('type' in content['users'][0]['favorite_pet'])
self.assertTrue('id' in content['users'][0]['favorite_pet'])

def test_multi_sideload_include(self):
url = (
'/cars/1/?include[]=name&include[]=country.short_name'
'&include[]=parts.name&include[]=parts.country.name'
)
response = self.client.get(url)
self.assertEqual(200, response.status_code)
content = json.loads(response.content.decode('utf-8'))
self.assertTrue('countries' in content)

country = None
for _ in content['countries']:
if _['id'] == 1:
country = _

self.assertTrue(country)
self.assertTrue('short_name' in country)
self.assertTrue('name' in country)

def test_query_counts(self):
# NOTE: Django doesn't seem to prefetch ContentType objects
# themselves, and rather caches internally. That means
Expand Down
1 change: 1 addition & 0 deletions tests/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
router.register_resource(viewsets.ProfileViewSet)
router.register_resource(viewsets.LocationViewSet)

router.register(r'cars', viewsets.CarViewSet)
router.register(r'cats', viewsets.CatViewSet)
router.register_resource(viewsets.DogViewSet)
router.register_resource(viewsets.HorseViewSet)
Expand Down
9 changes: 8 additions & 1 deletion tests/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from dynamic_rest.viewsets import DynamicModelViewSet
from tests.models import (
Car,
Cat,
Dog,
Group,
Expand All @@ -13,6 +14,7 @@
Zebra
)
from tests.serializers import (
CarSerializer,
CatSerializer,
DogSerializer,
GroupSerializer,
Expand All @@ -23,7 +25,7 @@
UserLocationSerializer,
UserSerializer,
ZebraSerializer
)
)


class UserViewSet(DynamicModelViewSet):
Expand Down Expand Up @@ -150,3 +152,8 @@ class ZebraViewSet(DynamicModelViewSet):
class PermissionViewSet(DynamicModelViewSet):
serializer_class = PermissionSerializer
queryset = Permission.objects.all()


class CarViewSet(DynamicModelViewSet):
serializer_class = CarSerializer
queryset = Car.objects.all()

0 comments on commit c0e17f2

Please sign in to comment.