Fakultas Ilmu Komputer UI
Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
ppl-fasilkom-ui
2020
PPL-C
PPTI-Mobile Apps Monitoring Wabah Tuberkolosis
Neza-Backend
Commits
c10e67ad
Commit
c10e67ad
authored
Apr 19, 2020
by
Dave Nathanael
Committed by
Jonathan Christopher Jakub
Apr 19, 2020
Browse files
Implement rework on model revisions
parent
20842e86
Changes
39
Hide whitespace changes
Inline
Side-by-side
apps/accounts/filters.py
View file @
c10e67ad
from
django_filters
import
FilterSet
,
CharFilter
ACCOUNT_FILTERSET_FIELDS
=
(
"name"
,
"email"
,
"phone_number"
,
"area"
,
"is_admin"
,
"is_verified"
,
"is_active"
,
)
from
apps.accounts.models
import
Account
ACCOUNT_SEARCH_FIELDS
=
(
"name"
,
"email"
,
"phone_number"
,
"area"
,
"is_admin"
,
"is_verified"
,
"is_active"
,
)
class
AccountFilter
(
FilterSet
):
username
=
CharFilter
(
field_name
=
"user__username"
)
class
Meta
:
model
=
Account
fields
=
[
"username"
,
"name"
,
"email"
,
"phone_number"
,
"area"
,
"is_admin"
,
"is_verified"
,
]
ACCOUNT_ORDERING_FIELDS
=
(
"name"
,
"email"
,
"phone_number"
,
"area"
,
"is_admin"
,
"is_verified"
,
"is_active"
,
"created_at"
,
)
apps/accounts/migrations/0002_auto_20200419_0156.py
0 → 100644
View file @
c10e67ad
# Generated by Django 3.0.1 on 2020-04-18 18:56
from
django.db
import
migrations
,
models
import
uuid
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
"accounts"
,
"0001_initial"
),
]
operations
=
[
migrations
.
CreateModel
(
name
=
"AccountHistory"
,
fields
=
[
(
"revision_id"
,
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
editable
=
False
,
primary_key
=
True
,
serialize
=
False
,
),
),
(
"object_id"
,
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
editable
=
False
)),
(
"action_type"
,
models
.
CharField
(
choices
=
[
(
"Create"
,
"Create"
),
(
"Edit"
,
"Edit"
),
(
"Delete"
,
"Delete"
),
],
max_length
=
64
,
),
),
(
"recorded_at"
,
models
.
DateTimeField
(
auto_now_add
=
True
)),
(
"user_id"
,
models
.
IntegerField
()),
(
"name"
,
models
.
CharField
(
max_length
=
128
)),
(
"email"
,
models
.
EmailField
(
max_length
=
128
)),
(
"phone_number"
,
models
.
CharField
(
max_length
=
64
)),
(
"area"
,
models
.
CharField
(
max_length
=
128
)),
(
"is_admin"
,
models
.
BooleanField
(
default
=
False
)),
(
"is_verified"
,
models
.
BooleanField
(
default
=
False
)),
(
"is_active"
,
models
.
BooleanField
(
default
=
False
)),
],
options
=
{
"verbose_name_plural"
:
"account histories"
,
"db_table"
:
"account_history"
,
"ordering"
:
[
"-recorded_at"
],
},
),
migrations
.
AlterModelOptions
(
name
=
"account"
,
options
=
{
"ordering"
:
[
"-created_at"
],
"verbose_name_plural"
:
"accounts"
},
),
migrations
.
AddField
(
model_name
=
"account"
,
name
=
"deleted_at"
,
field
=
models
.
DateTimeField
(
blank
=
True
,
null
=
True
),
),
]
apps/accounts/migrations/0003_accounthistory_author.py
0 → 100644
View file @
c10e67ad
# Generated by Django 3.0.1 on 2020-04-19 11:26
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
"accounts"
,
"0002_auto_20200419_0156"
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
"accounthistory"
,
name
=
"author"
,
field
=
models
.
UUIDField
(
null
=
True
),
),
]
apps/accounts/migrations/0004_account_author.py
0 → 100644
View file @
c10e67ad
# Generated by Django 3.0.1 on 2020-04-19 11:29
from
django.db
import
migrations
,
models
import
django.db.models.deletion
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
"accounts"
,
"0003_accounthistory_author"
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
"account"
,
name
=
"author"
,
field
=
models
.
ForeignKey
(
blank
=
True
,
null
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
DO_NOTHING
,
related_name
=
"account"
,
to
=
"accounts.Account"
,
),
),
]
apps/accounts/models.py
View file @
c10e67ad
...
...
@@ -2,10 +2,12 @@ import uuid
from
django.contrib.auth.models
import
User
from
django.db
import
models
from
apps.commons.managers
import
SoftDeleteManager
from
apps.commons.models
import
HistoryEnabledModel
,
HistoryModel
class
Account
(
models
.
Model
):
id
=
models
.
UUIDField
(
primary_key
=
True
,
default
=
uuid
.
uuid4
,
editable
=
False
)
user
=
models
.
OneToOneField
(
User
,
on_delete
=
models
.
CASCADE
)
class
AccountHistory
(
HistoryModel
):
user
_id
=
models
.
IntegerField
(
null
=
False
)
name
=
models
.
CharField
(
max_length
=
128
)
email
=
models
.
EmailField
(
max_length
=
128
)
phone_number
=
models
.
CharField
(
max_length
=
64
)
...
...
@@ -13,15 +15,46 @@ class Account(models.Model):
is_admin
=
models
.
BooleanField
(
default
=
False
)
is_verified
=
models
.
BooleanField
(
default
=
False
)
is_active
=
models
.
BooleanField
(
default
=
False
)
created_at
=
models
.
DateTimeField
(
auto_now_add
=
True
)
author
=
models
.
UUIDField
(
null
=
True
)
objects
=
models
.
Manager
()
class
Meta
:
db_table
=
"account_history"
verbose_name_plural
=
"account histories"
ordering
=
[
"-recorded_at"
]
def
__str__
(
self
):
return
f
"[History] Rev.
{
self
.
revision_id
}
-
{
self
.
name
}
"
class
Account
(
HistoryEnabledModel
):
user
=
models
.
OneToOneField
(
User
,
on_delete
=
models
.
CASCADE
)
name
=
models
.
CharField
(
max_length
=
128
)
email
=
models
.
EmailField
(
max_length
=
128
)
phone_number
=
models
.
CharField
(
max_length
=
64
)
area
=
models
.
CharField
(
max_length
=
128
)
is_admin
=
models
.
BooleanField
(
default
=
False
)
is_verified
=
models
.
BooleanField
(
default
=
False
)
is_active
=
models
.
BooleanField
(
default
=
False
)
author
=
models
.
ForeignKey
(
"self"
,
blank
=
True
,
null
=
True
,
on_delete
=
models
.
DO_NOTHING
,
related_name
=
"account"
,
)
history_class
=
AccountHistory
relation_fields
=
[
"user"
]
objects
=
SoftDeleteManager
()
class
Meta
:
db_table
=
"account"
verbose_name_plural
=
"accounts"
ordering
=
[
"-created_at"
]
def
__str__
(
self
):
if
self
.
is_admin
:
return
f
"[Admin]
{
self
.
user
.
username
}
"
return
f
"[Officer]
{
self
.
user
.
username
}
"
account_role
=
"[Admin]"
if
self
.
is_admin
else
"[Officer]"
return
f
"
{
account_role
}
{
self
.
user
.
username
}
"
apps/accounts/serializers.py
View file @
c10e67ad
...
...
@@ -5,7 +5,7 @@ from apps.accounts.models import Account
class
AccountSerializer
(
serializers
.
ModelSerializer
):
username
=
serializers
.
CharField
(
source
=
"user.username"
,
re
quired
=
Fals
e
)
username
=
serializers
.
CharField
(
source
=
"user.username"
,
re
ad_only
=
Tru
e
)
class
Meta
:
model
=
Account
...
...
@@ -26,6 +26,10 @@ class AccountSerializer(serializers.ModelSerializer):
"username"
,
]
def
save
(
self
):
account
=
self
.
context
.
get
(
'request'
).
user
.
account
super
(
AccountSerializer
,
self
).
save
(
author
=
account
)
class
AccountRegisterSerializer
(
serializers
.
ModelSerializer
):
username
=
serializers
.
CharField
(
max_length
=
128
)
...
...
@@ -47,3 +51,7 @@ class AccountRegisterSerializer(serializers.ModelSerializer):
def
validate_password
(
self
,
value
):
password_validation
.
validate_password
(
value
)
return
value
def
to_representation
(
self
,
instance
):
serializer
=
AccountSerializer
(
instance
)
return
serializer
.
data
apps/accounts/tests/test_units/test_accounts.py
View file @
c10e67ad
...
...
@@ -6,7 +6,7 @@ from rest_framework.authtoken.models import Token
from
rest_framework.test
import
APITestCase
,
APIClient
from
apps.accounts.tests.factories.accounts
import
AccountFactory
,
UserFactory
from
apps.accounts.models
import
Account
from
apps.accounts.models
import
Account
,
AccountHistory
from
apps.constants
import
(
HEADER_PREFIX
,
ACTIVITY_TYPE_CREATE
,
...
...
@@ -17,20 +17,21 @@ from apps.constants import (
class
AccountViewTest
(
APITestCase
):
@
classmethod
def
setUpTestData
(
self
):
self
.
BASE_URL
=
"/accounts/"
self
.
PROFILE_URL
=
self
.
BASE_URL
+
"me/"
def
setUpTestData
(
cls
):
cls
.
BASE_URL
=
"/accounts/"
cls
.
PROFILE_URL
=
cls
.
BASE_URL
+
"me/"
cls
.
LOGS_URL
=
"/logs/"
self
.
user_1
=
UserFactory
(
username
=
"user_1"
,
password
=
"justpass"
)
self
.
user_2
=
UserFactory
(
username
=
"user_2"
,
password
=
"justpass"
)
self
.
admin
=
AccountFactory
(
admin
=
True
,
user
=
self
.
user_1
)
self
.
officer
=
AccountFactory
(
admin
=
False
,
user
=
self
.
user_2
)
self
.
accounts
=
[
self
.
admin
,
self
.
officer
]
cls
.
user_1
=
UserFactory
(
username
=
"user_1"
,
password
=
"justpass"
)
cls
.
user_2
=
UserFactory
(
username
=
"user_2"
,
password
=
"justpass"
)
cls
.
admin
=
AccountFactory
(
admin
=
True
,
user
=
cls
.
user_1
)
cls
.
officer
=
AccountFactory
(
admin
=
False
,
user
=
cls
.
user_2
)
cls
.
accounts
=
[
cls
.
admin
,
cls
.
officer
]
self
.
token_1
,
_
=
Token
.
objects
.
get_or_create
(
user
=
self
.
user_1
)
self
.
token_2
,
_
=
Token
.
objects
.
get_or_create
(
user
=
self
.
user_2
)
cls
.
token_1
,
_
=
Token
.
objects
.
get_or_create
(
user
=
cls
.
user_1
)
cls
.
token_2
,
_
=
Token
.
objects
.
get_or_create
(
user
=
cls
.
user_2
)
self
.
faker
=
Faker
()
cls
.
faker
=
Faker
()
def
setUp
(
self
):
self
.
client
=
APIClient
(
HTTP_AUTHORIZATION
=
HEADER_PREFIX
+
self
.
token_1
.
key
)
...
...
@@ -42,6 +43,12 @@ class AccountViewTest(APITestCase):
officer_str
=
f
"[Officer]
{
self
.
officer
.
user
.
username
}
"
self
.
assertEqual
(
officer_str
,
str
(
self
.
officer
))
def
test_history_string_representation
(
self
):
history
=
AccountHistory
.
objects
.
all
().
first
()
history_str
=
f
"[History] Rev.
{
history
.
revision_id
}
-
{
history
.
name
}
"
self
.
assertEqual
(
history_str
,
str
(
history
))
def
test_list_all_accounts_success
(
self
):
url
=
self
.
BASE_URL
...
...
@@ -51,33 +58,6 @@ class AccountViewTest(APITestCase):
self
.
assertIn
(
'"user_1"'
,
response_string
)
self
.
assertIn
(
'"user_2"'
,
response_string
)
def
test_list_all_accounts_paginate_failed
(
self
):
url
=
self
.
BASE_URL
+
"?page=100"
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_404_NOT_FOUND
)
response_string
=
response
.
rendered_content
.
decode
(
"utf-8"
)
self
.
assertIn
(
'"detail":"Invalid page."'
,
response_string
)
def
test_list_all_accounts_filter_success
(
self
):
url
=
self
.
BASE_URL
+
"?username="
+
self
.
user_1
.
username
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_200_OK
)
response_string
=
response
.
rendered_content
.
decode
(
"utf-8"
)
self
.
assertIn
(
'"count":1'
,
response_string
)
def
test_list_all_accounts_filter_failed
(
self
):
url
=
self
.
BASE_URL
+
"?username=1234567890"
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_200_OK
)
response_string
=
response
.
rendered_content
.
decode
(
"utf-8"
)
self
.
assertIn
(
'"count":0'
,
response_string
)
def
test_retrieve_account_success
(
self
):
url
=
self
.
BASE_URL
+
str
(
self
.
officer
.
id
)
+
"/"
response
=
self
.
client
.
get
(
url
)
...
...
@@ -142,32 +122,14 @@ class AccountViewTest(APITestCase):
# Have account creation log for the new officer
new_officer_id
=
Account
.
objects
.
filter
(
email
=
_account_id
)[
0
].
id
response
=
self
.
client
.
get
(
"/logs/"
)
response
=
self
.
client
.
get
(
self
.
LOGS_URL
)
response_string
=
response
.
rendered_content
.
decode
(
"utf-8"
)
self
.
assertIn
(
'"object_id":"{
}"'
.
format
(
new_officer_id
)
,
response_string
)
self
.
assertIn
(
f
'"object_id":"
{
new_officer_id
}
"'
,
response_string
)
self
.
assertIn
(
'"action_type":"{
}"'
.
format
(
ACTIVITY_TYPE_CREATE
)
,
response_string
f
'"action_type":"
{
ACTIVITY_TYPE_CREATE
}
"'
,
response_string
)
def
test_username_lowercased
(
self
):
url
=
self
.
BASE_URL
data
=
{
"name"
:
self
.
faker
.
name
(),
"username"
:
"aBcDeFgH"
,
"password"
:
"justpass"
,
"email"
:
self
.
faker
.
email
(),
"phone_number"
:
self
.
faker
.
phone_number
(),
"area"
:
self
.
faker
.
city
(),
"is_admin"
:
False
,
}
self
.
client
.
post
(
path
=
url
,
data
=
data
,
format
=
"json"
,
)
self
.
assertTrue
(
Account
.
objects
.
filter
(
user__username
=
"abcdefgh"
).
exists
())
def
test_create_new_account_fails_with_poor_password
(
self
):
url
=
self
.
BASE_URL
_account_id
=
self
.
faker
.
email
()
...
...
@@ -210,11 +172,11 @@ class AccountViewTest(APITestCase):
)
# Have account update log
response
=
self
.
client
.
get
(
"/logs/"
)
response
=
self
.
client
.
get
(
self
.
LOGS_URL
)
response_string
=
response
.
rendered_content
.
decode
(
"utf-8"
)
self
.
assertIn
(
'"object_id":"{
}"'
.
format
(
self
.
officer
.
id
)
,
response_string
)
self
.
assertIn
(
'"action_type":"{
}"'
.
format
(
ACTIVITY_TYPE_EDIT
)
,
response_string
)
self
.
assertIn
(
f
'"object_id":"
{
self
.
officer
.
id
}
"'
,
response_string
)
self
.
assertIn
(
f
'"action_type":"
{
ACTIVITY_TYPE_EDIT
}
"'
,
response_string
)
def
test_edit_account_fail_without_complete_fields
(
self
):
url
=
self
.
BASE_URL
+
str
(
self
.
officer
.
id
)
+
"/"
...
...
@@ -231,19 +193,32 @@ class AccountViewTest(APITestCase):
def
test_delete_success
(
self
):
url
=
self
.
BASE_URL
+
str
(
self
.
officer
.
id
)
+
"/"
account_prev_count
=
Account
.
objects
.
all
().
count
()
account_history_prev_count
=
AccountHistory
.
objects
.
all
().
count
()
account_deleted_prev_count
=
Account
.
objects
.
deleted
().
count
()
response
=
self
.
client
.
delete
(
url
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_20
0_OK
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_20
4_NO_CONTENT
)
# Have account deletion log
response
=
self
.
client
.
get
(
"/logs/"
)
response
=
self
.
client
.
get
(
self
.
LOGS_URL
)
response_string
=
response
.
rendered_content
.
decode
(
"utf-8"
)
self
.
assertIn
(
'"object_id":"{
}"'
.
format
(
self
.
officer
.
id
)
,
response_string
)
self
.
assertIn
(
f
'"object_id":"
{
self
.
officer
.
id
}
"'
,
response_string
)
self
.
assertIn
(
'"action_type":"{
}"'
.
format
(
ACTIVITY_TYPE_DELETE
)
,
response_string
f
'"action_type":"
{
ACTIVITY_TYPE_DELETE
}
"'
,
response_string
)
account_current_count
=
Account
.
objects
.
all
().
count
()
account_history_current_count
=
AccountHistory
.
objects
.
all
().
count
()
account_deleted_current_count
=
Account
.
objects
.
deleted
().
count
()
self
.
assertEqual
(
account_current_count
,
account_prev_count
-
1
)
self
.
assertEqual
(
account_history_current_count
,
account_history_prev_count
+
1
)
self
.
assertEqual
(
account_deleted_current_count
,
account_deleted_prev_count
+
1
)
def
test_retrieve_current_profile_success
(
self
):
url
=
self
.
PROFILE_URL
...
...
apps/accounts/views.py
View file @
c10e67ad
...
...
@@ -3,17 +3,22 @@ from django.shortcuts import get_object_or_404
from
django_filters.rest_framework
import
DjangoFilterBackend
from
rest_framework
import
status
,
viewsets
from
rest_framework.decorators
import
action
from
rest_framework.filters
import
SearchFilter
,
OrderingFilter
from
rest_framework.pagination
import
PageNumberPagination
from
rest_framework.response
import
Response
from
apps.accounts.filters
import
AccountFilter
from
apps.accounts.filters
import
(
ACCOUNT_FILTERSET_FIELDS
,
ACCOUNT_ORDERING_FIELDS
,
ACCOUNT_SEARCH_FIELDS
,
)
from
apps.accounts.models
import
Account
from
apps.accounts.serializers
import
(
AccountSerializer
,
AccountRegisterSerializer
,
)
from
apps.commons.permissions
import
(
Is
SelfOrAdministrator
,
Is
Authenticated
,
CreateOnly
,
)
from
apps.constants
import
(
...
...
@@ -22,85 +27,39 @@ from apps.constants import (
ACTIVITY_TYPE_EDIT
,
ACTIVITY_TYPE_DELETE
,
)
from
apps.logs.models
import
Log
class
AccountViewSet
(
viewsets
.
ViewSet
):
queryset
=
Account
.
objects
.
all
().
select_related
(
"user"
).
order_by
(
"-created_at"
)
filter_backends
=
(
DjangoFilterBackend
,)
permission_classes
=
[
IsSelfOrAdministrator
|
CreateOnly
,
]
def
list
(
self
,
request
):
paginator
=
PageNumberPagination
()
filtered_set
=
AccountFilter
(
request
.
GET
,
queryset
=
self
.
queryset
).
qs
context
=
paginator
.
paginate_queryset
(
filtered_set
,
request
)
serializer
=
AccountSerializer
(
context
,
many
=
True
)
return
paginator
.
get_paginated_response
(
serializer
.
data
)
def
retrieve
(
self
,
request
,
pk
=
None
):
instance
=
get_object_or_404
(
self
.
queryset
,
pk
=
pk
)
self
.
check_object_permissions
(
request
,
instance
)
serializer
=
AccountSerializer
(
instance
)
return
Response
(
serializer
.
data
,
status
=
status
.
HTTP_200_OK
)
def
create
(
self
,
request
):
serializer
=
AccountRegisterSerializer
(
data
=
request
.
data
)
serializer
.
is_valid
(
raise_exception
=
True
)
class
AccountViewSet
(
viewsets
.
ModelViewSet
):
serializer_class
=
AccountSerializer
queryset
=
Account
.
objects
.
all
()
filter_backends
=
(
DjangoFilterBackend
,
SearchFilter
,
OrderingFilter
)
filterset_fields
=
ACCOUNT_FILTERSET_FIELDS
permission_classes
=
(
IsAuthenticated
|
CreateOnly
,
)
pagination_class
=
PageNumberPagination
search_fields
=
ACCOUNT_SEARCH_FIELDS
ordering_fields
=
ACCOUNT_ORDERING_FIELDS
def
get_queryset
(
self
):
user
=
self
.
request
.
user
account
=
Account
.
objects
.
filter
(
user
=
user
)
if
account
.
first
().
is_admin
:
return
Account
.
objects
.
all
()
return
account
def
get_serializer_class
(
self
):
if
self
.
action
in
[
'create'
]:
return
AccountRegisterSerializer
return
AccountSerializer
def
perform_create
(
self
,
serializer
):
username
=
serializer
.
validated_data
.
pop
(
"username"
).
lower
()
password
=
serializer
.
validated_data
.
pop
(
"password"
)
user
=
User
.
objects
.
create_user
(
username
=
username
,
password
=
password
)
account
=
Account
.
objects
.
create
(
user
=
user
,
**
serializer
.
validated_data
)
# Add account creation log
Log
.
objects
.
create
(
model_name
=
MODEL_NAME_ACCOUNT
,
object_id
=
account
.
id
,
action_type
=
ACTIVITY_TYPE_CREATE
,
author
=
account
,
)
Account
.
objects
.
create
(
user
=
user
,
**
serializer
.
validated_data
)
return
Response
(
AccountSerializer
(
account
).
data
,
status
=
status
.
HTTP_201_CREATED
,
)
def
update
(
self
,
request
,
pk
=
None
):
instance
=
get_object_or_404
(
self
.
queryset
,
pk
=
pk
)
self
.
check_object_permissions
(
request
,
instance
)
serializer
=
AccountSerializer
(
instance
,
data
=
request
.
data
)
serializer
.
is_valid
(
raise_exception
=
True
)
serializer
.
save
()
# Add account update log
Log
.
objects
.
create
(
model_name
=
MODEL_NAME_ACCOUNT
,
object_id
=
pk
,
action_type
=
ACTIVITY_TYPE_EDIT
,
author
=
request
.
user
.
account
,
)
return
Response
(
serializer
.
data
,
status
=
status
.
HTTP_200_OK
)
def
destroy
(
self
,
request
,
pk
=
None
):
instance
=
get_object_or_404
(
self
.
queryset
,
pk
=
pk
)
self
.
check_object_permissions
(
request
,
instance
)
instance
.
delete
()
# Add account deletion log
Log
.
objects
.
create
(
model_name
=
MODEL_NAME_ACCOUNT
,
object_id
=
pk
,
action_type
=
ACTIVITY_TYPE_DELETE
,
author
=
request
.
user
.
account
,
)
serializer
=
AccountSerializer
(
instance
=
instance
)
return
Response
(
serializer
.
data
,
status
=
status
.
HTTP_200_OK
)
def
perform_destroy
(
self
,
instance
):
instance
.
delete
(
author
=
self
.
request
.
user
.
account
)
@
action
(
detail
=
False
,
methods
=
[
"get"
],
url_path
=
"me"
)
def
profile
(
self
,
request
):
...
...
apps/cases/filters.py
View file @
c10e67ad
from
django_filters
import
FilterSet
from
apps.cases.models
import
(
CaseSubject
,
InvestigationCase
,
MonitoringCase
,
)
class
CaseSubjectFilter
(
FilterSet
):
class
Meta
:
model
=
CaseSubject
fields
=
[
"age"
,
"is_male"
,
"address"
,
"district"
,
"sub_district"
,
]