Skip to content

Commit 1e1b5e9

Browse files
authored
Created by user drf mixin (#13)
* add DefaultUserCreateMixin * better explanation of DefaultUsercreateMixin * add tests for CreateUser mixin stuff..! * fix drf req
1 parent 031f9c6 commit 1e1b5e9

File tree

9 files changed

+125
-10
lines changed

9 files changed

+125
-10
lines changed

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
django-ckc [<img src="https://ckcollab.com/assets/images/badges/badge.svg" alt="CKC" height="20">](https://ckcollab.com)
22
==========
3-
tools, utilities, etc. we use across projects @ [ckc](https://ckcollab.com)
3+
tools, utilities
4+
, etc. we use across projects @ [ckc](https://ckcollab.com)
45

56

67
## installing
@@ -29,10 +30,12 @@ $ ./setup.py sdist
2930
$ twine upload dist/*
3031
```
3132

32-
## run tests
33+
## tests
3334

3435
```bash
35-
$ docker-compose exec django py.test
36+
# get into a virtual env of some kind
37+
$ pip install -r requirements.txt
38+
$ pytest
3639
```
3740

3841
## what's in this
@@ -62,4 +65,3 @@ class MySerializer(DefaultUserCreateMixin, ModelSerializer):
6265
| command | description|
6366
| :--- | :----: |
6467
| `upload_file <source> <destination>` | uses `django-storages` settings to upload a file |
65-

ckc/serializers.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
class DefaultUserCreateMixin:
2+
"""This will automatically set `YourModel.created_by` to `request.user`. To override which
3+
attribute the user is written to, add a `user_field` to your classes Meta information
4+
Example:
5+
class YourModel(models.Model):
6+
owner = models.ForeignKey(User, on_delete=models.CASCADE)
7+
8+
class MySerializer(DefaultUserCreateMixin, ModelSerializer):
9+
class Meta:
10+
model = YourModel
11+
# YourModel.owner = a foreign key to request.user which differs from the
12+
# default `created_by`
13+
user_field = 'owner'
14+
"""
15+
def create(self, validated_data):
16+
# get name of the user field we'll be writing request.user to, default created_by
17+
user_field = getattr(self.Meta, 'user_field', 'created_by')
18+
19+
assert hasattr(self.Meta.model, user_field), f"{self.Meta.model} needs to have field {user_field} so " \
20+
f"DefaultUserCreateMixin can write to it"
21+
22+
if user_field not in validated_data:
23+
if 'request' not in self.context:
24+
raise Exception('self.context does not contain "request". Have you overwritten get_serializer_context '
25+
'and overwrote context?')
26+
validated_data[user_field] = self.context['request'].user
27+
return super().create(validated_data)

requirements.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
# python packaging
12
twine==3.1.1
3+
4+
# django stuff
25
Django==3.0.7
6+
djangorestframework==3.12.2
7+
8+
# tests
39
pytest==5.4.1
410
pytest-django==3.9.0
511
pytest-pythonpath==0.7.3

testproject/testapp/models.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1+
from django.contrib.auth import get_user_model
12
from django.db import models
23

34
from ckc.models import SoftDeletableModel
45

56

6-
class TestModel(SoftDeletableModel):
7+
User = get_user_model()
8+
9+
10+
class AModel(SoftDeletableModel):
711
title = models.CharField(max_length=255, default="I'm a test!")
12+
13+
14+
class ModelWithACreator(models.Model):
15+
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
16+
17+
18+
class ModelWithADifferentNamedCreator(models.Model):
19+
owner = models.ForeignKey(User, on_delete=models.CASCADE)

testproject/testapp/serializers.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from rest_framework.serializers import ModelSerializer
2+
3+
from ckc.serializers import DefaultUserCreateMixin
4+
from testapp.models import ModelWithACreator, ModelWithADifferentNamedCreator
5+
6+
7+
class TestModelWithACreatorSerializer(DefaultUserCreateMixin, ModelSerializer):
8+
class Meta:
9+
model = ModelWithACreator
10+
fields = []
11+
12+
13+
class TestModelWithADifferentNamedCreatorSerializer(DefaultUserCreateMixin, ModelSerializer):
14+
class Meta:
15+
model = ModelWithADifferentNamedCreator
16+
fields = []
17+
user_field = 'owner'

testproject/testapp/viewsets.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from rest_framework.viewsets import ModelViewSet
2+
3+
from testapp.models import ModelWithACreator, ModelWithADifferentNamedCreator
4+
from testapp.serializers import TestModelWithACreatorSerializer, TestModelWithADifferentNamedCreatorSerializer
5+
6+
7+
class TestModelWithACreatorViewSet(ModelViewSet):
8+
queryset = ModelWithACreator.objects.all()
9+
serializer_class = TestModelWithACreatorSerializer
10+
11+
12+
class TestModelWithADifferentNamedCreatorViewSet(ModelViewSet):
13+
queryset = ModelWithADifferentNamedCreator.objects.all()
14+
serializer_class = TestModelWithADifferentNamedCreatorSerializer

testproject/urls.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,10 @@
1-
urlpatterns = []
1+
from rest_framework import routers
2+
3+
from testapp.viewsets import TestModelWithACreatorViewSet, TestModelWithADifferentNamedCreatorViewSet
4+
5+
6+
router = routers.SimpleRouter()
7+
router.register(r'creators', TestModelWithACreatorViewSet)
8+
router.register(r'creators-alternative', TestModelWithADifferentNamedCreatorViewSet)
9+
10+
urlpatterns = router.urls
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from django.contrib.auth import get_user_model
2+
from django.urls import reverse
3+
from rest_framework.test import APITestCase
4+
5+
from testapp.models import ModelWithACreator, ModelWithADifferentNamedCreator
6+
7+
User = get_user_model()
8+
9+
10+
class TestDefaultUserCreateMixin(APITestCase):
11+
12+
def setUp(self):
13+
self.user = User.objects.create_user(username="test", password="test")
14+
self.client.force_authenticate(self.user)
15+
16+
def test_default_create_user_mixin_works(self):
17+
from django.urls import get_resolver
18+
print(get_resolver().reverse_dict.keys())
19+
20+
resp = self.client.post(reverse('modelwithacreator-list'))
21+
print(resp.content)
22+
assert resp.status_code == 201
23+
assert ModelWithACreator.objects.get(created_by=self.user)
24+
25+
def test_default_create_user_mixin_with_different_field_name_works(self):
26+
resp = self.client.post(reverse('modelwithadifferentnamedcreator-list'))
27+
assert resp.status_code == 201
28+
assert ModelWithADifferentNamedCreator.objects.get(owner=self.user)
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
from django.test import TestCase
22

3-
from testapp.models import TestModel
3+
from testapp.models import AModel
44

55

66
class TestSoftDelete(TestCase):
77

88
def test_soft_delete_model_doesnt_really_delete(self):
9-
instance = TestModel.objects.create()
9+
instance = AModel.objects.create()
1010
instance.delete()
11-
assert not TestModel.objects.filter(pk=instance.pk).exists()
12-
assert TestModel.all_objects.filter(pk=instance.pk).exists()
11+
assert not AModel.objects.filter(pk=instance.pk).exists()
12+
assert AModel.all_objects.filter(pk=instance.pk).exists()

0 commit comments

Comments
 (0)