I have encountered a weird behaviour where one of the admin views which is quite simple and straight forward takes a while to load up until an nginx timeout in prod.
Through Django debug toolbar and new relic I have found that the guilty code is the field_sets block in Django templates, which makes it way harder to debug.
On production, when the view does load, it is empty.
This is the first time I encounter something like that, and thus I decided to reach out to the community for help.
The model:
from datetime import timedelta
from decimal import Decimal
from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django.utils import timezone
from django.utils.crypto import get_random_string
from django.utils.translation import ugettext as _
from core.common.models import (SoftDeletableModel, TimeStampedModel,
UniqueFieldMixin, FlagableMixin)
from core.models import Pair
from payments.utils import money_format
from ticker.models import Price
from django.core.exceptions import ValidationError
class Order(TimeStampedModel, SoftDeletableModel,
UniqueFieldMixin, FlagableMixin):
USD = "USD"
RUB = "RUB"
EUR = "EUR"
BTC = "BTC"
EXCHANGE = 2
BUY = 1
SELL = 0
TYPES = (
(SELL, 'SELL'),
(BUY, 'BUY'),
(EXCHANGE, 'EXCHANGE'),
)
_order_type_help = '{} - {}<br/>{} - {}<br/>{} - {}<br/>'.format(
'BUY', 'Customer is giving fiat, and getting crypto money.',
'SELL', 'Customer is giving crypto and getting fiat money',
'EXCHANGE', 'Customer is exchanging different kinds of crypto '
'currencies'
)
PAID_UNCONFIRMED = -1
CANCELED = 0
INITIAL = 1
PAID = 2
RELEASED = 3
COMPLETED = 4
STATUS_TYPES = (
(PAID_UNCONFIRMED, 'UNCONFIRMED PAYMENT'),
(CANCELED, 'CANCELED'),
(INITIAL, 'INITIAL'),
(PAID, 'PAID'),
(RELEASED, 'RELEASED'),
(COMPLETED, 'COMPLETED'),
)
IN_PAID = [PAID, RELEASED, COMPLETED]
IN_RELEASED = [RELEASED, COMPLETED]
_could_be_paid_msg = 'Could be paid by crypto transaction or fiat ' \
'payment, depending on order_type.'
_order_status_help =\
'{} - {}<br/>{} - {}<br/>{} - {}<br/>' \
'{} - {}<br/>{} - {}<br/>{} - {}<br/>'\
.format(
'INITIAL', 'Initial status of the order.',
'PAID', 'Order is Paid by customer. ' + _could_be_paid_msg,
'PAID_UNCONFIRMED', 'Order is possibly paid (unconfirmed crypto '
'transaction or fiat payment is to small to '
'cover the order.)',
'RELEASED', 'Order is paid by service provider. ' + _could_be_paid_msg,
'COMPLETED', 'All statuses of the order is completed',
'CANCELED', 'Order is canceled.'
)
# Todo: inherit from BTC base?, move lengths to settings?
order_type = models.IntegerField(
choices=TYPES, default=BUY,
# help_text=_order_type_help
)
status = models.IntegerField(choices=STATUS_TYPES, default=INITIAL,
# help_text=_order_status_help
)
amount_base = models.DecimalField(max_digits=18, decimal_places=8)
amount_quote = models.DecimalField(max_digits=18, decimal_places=8)
payment_window = models.IntegerField(default=settings.PAYMENT_WINDOW)
user = models.ForeignKey(User, related_name='orders')
unique_reference = models.CharField(
max_length=settings.UNIQUE_REFERENCE_MAX_LENGTH)
admin_comment = models.CharField(max_length=200)
payment_preference = models.ForeignKey('payments.PaymentPreference',
default=None,
null=True)
withdraw_address = models.ForeignKey('core.Address',
null=True,
related_name='order_set',
default=None)
is_default_rule = models.BooleanField(default=False)
from_default_rule = models.BooleanField(default=False)
pair = models.ForeignKey(Pair)
price = models.ForeignKey(Price, null=True, blank=True)
user_marked_as_paid = models.BooleanField(default=False)
system_marked_as_paid = models.BooleanField(default=False)
class Meta:
ordering = ['-created_on']
# unique_together = ['deleted', 'unique_reference']
def validate_unique(self, exclude=None):
# TODO: exclude expired?
if not self.deleted and \
Order.objects.exclude(pk=self.pk).filter(
unique_reference=self.unique_reference,
deleted=False).exists():
raise ValidationError(
'Un-deleted order with same reference exists')
super(Order, self).validate_unique(exclude=exclude)
def _types_range_constraint(self, field, types):
""" This is used for validating IntegerField's with choices.
Assures that value is in range of choices.
"""
if field > max([i[0] for i in types]):
raise ValidationError(_('Invalid order type choice'))
elif field < min([i[0] for i in types]):
raise ValidationError(_('Invalid order type choice'))
def _validate_fields(self):
self._types_range_constraint(self.order_type, self.TYPES)
self._types_range_constraint(self.status, self.STATUS_TYPES)
def clean(self, *args, **kwargs):
self._validate_fields()
super(Order, self).clean(*args, **kwargs)
def save(self, *args, **kwargs):
self._validate_fields()
if not self.unique_reference:
self.unique_reference = \
self.gen_unique_value(
lambda x: get_random_string(x),
lambda x: Order.objects.filter(unique_reference=x).count(),
settings.UNIQUE_REFERENCE_LENGTH
)
if self.status == self.INITIAL:
self.convert_coin_to_cash()
super(Order, self).save(*args, **kwargs)
def _not_supported_exchange_msg(self):
msg = _('Sorry, we cannot convert {} to {}'.format(
self.currency_from.code, self.currency_to.code
))
return msg
def convert_coin_to_cash(self):
self.amount_base = Decimal(self.amount_base)
price = Price.objects.filter(pair=self.pair).last()
self.price = price
# For annotations
amount_quote = None
if self.order_type == Order.BUY:
amount_quote = self.amount_base * price.ticker.ask
elif self.order_type == Order.SELL:
amount_quote = self.amount_base * price.ticker.bid
self.amount_quote = money_format(amount_quote)
#property
def is_buy(self):
return self.order_type == Order.BUY
#property
def payment_deadline(self):
"""returns datetime of payment_deadline (creation + payment_window)"""
# TODO: Use this for pay until message on 'order success' screen
return self.created_on + timedelta(minutes=self.payment_window)
#property
def expired(self):
"""Is expired if payment_deadline is exceeded and it's not paid yet"""
# TODO: validate this business rule
# TODO: Refactor, it is unreasonable to have different standards of
# time in the DB
return (timezone.now() > self.payment_deadline) and (
self.status not in Order.IN_PAID)
#property
def payment_status_frozen(self):
"""return a boolean indicating if order can be updated
Order is frozen if it is expired or has been paid
"""
# TODO: validate this business rule
return self.expired or self.status in Order.IN_RELEASED
#property
def withdrawal_address_frozen(self):
"""return bool whether the withdraw address can
be changed"""
return self.status in Order.IN_RELEASED
def __str__(self):
return "{} {} pair:{} base:{} quote:{} status:{}".format(
self.user.username or self.user.profile.phone,
self.get_order_type_display(),
self.pair.name,
self.amount_base,
self.amount_quote,
self.get_status_display()
)
If your model has a ForeignKey or ManyToManyField to a model with many items, then it can it can be very slow to render select box for that field.
You can improve performance by adding these field(s) to raw_id_fields.
In your case, since the price list is very long, you could do:
class OrderAdmin(admin.ModelAdmin):
raw_id_fields = ['price']
...
Related
Apologies if this has already been answered elsewhere. I cannot find an answer which I can retrofit into my situation.
I'm new to django so I feel the problem is me not getting a fundamental grasp of a presumably basic concept here...
Using DRF and pytest-django, i'm trying to be diligent and write tests along the way before it becomes too time consuming to manually test. I can see it snowballing pretty quickly.
The issue I face is when I try to test the creation of a Catalogue, I can't get it to pass an User instance to the mandatory field 'created_by'. The logic works fine when I test manually, but writing the test itself is causing me headaches.
Many thanks in advance!
The error is:
TypeError: Cannot encode None for key 'created_by' as POST data. Did you mean to pass an empty string or omit the value?
Code provided.
# core/models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
email = models.EmailField(unique=True)
# workshop/models.py
from django.conf import settings
from django.db import models
class Catalogue(models.Model):
STATE_DRAFT = 'DRAFT'
STATE_PUBLISHED_PRIVATE = 'PUBPRIV'
STATE_PUBLISHED_PUBLIC = 'PUBPUB'
STATE_CHOICES = [
(STATE_DRAFT, 'Draft'),
(STATE_PUBLISHED_PRIVATE, 'Published (Private)'),
(STATE_PUBLISHED_PUBLIC, 'Published (Public)')
]
company = models.ForeignKey(Company, on_delete=models.PROTECT)
title = models.CharField(max_length=255)
description = models.TextField(null=True, blank=True)
state = models.CharField(
max_length=10, choices=STATE_CHOICES, default=STATE_DRAFT)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
created_on = models.DateTimeField(auto_now_add=True)
def __str__(self) -> str:
return self.title
class CatalogueItem(models.Model):
TYPE_GOODS = 'GOODS'
TYPE_SERVICES = 'SERVICES'
TYPE_GOODS_AND_SERVICES = 'GOODS_AND_SERVICES'
TYPE_CHOICES = [
(TYPE_GOODS, 'Goods'),
(TYPE_SERVICES, 'Services'),
(TYPE_GOODS_AND_SERVICES, 'Goods & Services')
]
catalogue = models.ForeignKey(
Catalogue, on_delete=models.CASCADE, related_name='catalogueitems')
type = models.CharField(
max_length=50, choices=TYPE_CHOICES, default=TYPE_GOODS)
name = models.CharField(max_length=255)
description = models.TextField()
unit_price = models.DecimalField(max_digits=9, decimal_places=2)
can_be_discounted = models.BooleanField(default=True)
def __str__(self) -> str:
return self.name
#property
def item_type(self):
return self.get_type_display()
# workshop/serializers.py
class CatalogueSerializer(serializers.ModelSerializer):
catalogueitems = SimpleCatalogueItemSerializer(
many=True, read_only=True)
created_on = serializers.DateTimeField(read_only=True)
created_by = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
# depth = 1
model = Catalogue
fields = ['id', 'title', 'description',
'state', 'catalogueitems', 'created_by', 'created_on']
def create(self, validated_data):
company_id = self.context['company_id']
user = self.context['user']
return Catalogue.objects.create(company_id=company_id, created_by=user, **validated_data)
# workshop/views.py
class CatalogueViewSet(ModelViewSet):
serializer_class = CatalogueSerializer
def get_permissions(self):
if self.request.method in ['PATCH', 'PUT', 'DELETE', 'POST']:
return [IsAdminUser()]
return [IsAuthenticated()]
def get_queryset(self):
user = self.request.user
if user.is_staff:
return Catalogue.objects.prefetch_related('catalogueitems__catalogue').filter(company_id=self.kwargs['company_pk'])
elif user.is_authenticated:
return Catalogue.objects.filter(company_id=self.kwargs['company_pk'], state='PUBPUB')
def get_serializer_context(self):
company_id = self.kwargs['company_pk']
return {'company_id': company_id, 'user': self.request.user}
# workshop/tests/conftest.py
from core.models import User
from rest_framework.test import APIClient
import pytest
#pytest.fixture
def api_client():
return APIClient()
#pytest.fixture
def authenticate(api_client):
def do_authenticate(is_staff=False):
return api_client.force_authenticate(user=User(is_staff=is_staff))
return do_authenticate
# workshop/tests/test_catalogues.py
from core.models import User
from workshop.models import Catalogue
from rest_framework import status
import pytest
#pytest.fixture
def create_catalogue(api_client):
def do_create_catalogue(catalogue):
return api_client.post('/companies/1/catalogues/', catalogue)
return do_create_catalogue
class TestCreateCatalogue:
def test_if_admin_can_create_catalogue_returns_201(self, authenticate, create_catalogue):
user = authenticate(is_staff=True)
response = create_catalogue(
{'title': 'a', 'description': 'a', 'state': 'DRAFT','created_by':user})
assert response.status_code == status.HTTP_201_CREATED
I think you may have a problem with the user that you are using to do the test,
when you call authenticate it returns a client which is not the same as a user.
then you run the authenticate and log in as a generic user. Try making another fixture that creates a user first, authenticate with that user to return the client and then post that user you created to create_catalogue
from django.conf import settings
#pytest.fixture
def create_user() -> User:
return settings.AUTH_USER_MODEL.objects.create(
username="Test User", password="Test Password", email="testuser#example.com"
)
#pytest.fixture
def authenticate(api_client):
def do_authenticate(create_user):
return api_client.force_authenticate(create_user)
return do_authenticate
class TestCreateCatalogue:
def test_if_admin_can_create_catalogue_returns_201(self, authenticate, create_user create_catalogue):
user = authenticate(create_user)
response = create_catalogue(
{'title': 'a', 'description': 'a', 'state': 'DRAFT','created_by':create_user})
assert response.status_code == status.HTTP_201_CREATED
I try to implement validators for Django project
I've defined a file validators.py in myapp
I add validators=[validate_test] for my ran_num field
but got an error
I have another question: is it possible to control field BEFORE form submission?
validators.py
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
def validate_test(self, value):
if value == 'PPP': # just test
raise ValidationError(
_("Test"),
code='Test',
params={},
)
forms.py
from .validators import validate_date
class EditForm(forms.ModelForm):
def __init__(self, request, *args, **kwargs):
super(EditForm, self).__init__(*args, **kwargs)
self.request = request
self.fields["asp_ent_loc"] = forms.ChoiceField(label = _("Site concerned by the operation"), widget=forms.Select, choices=SITE_CONCERNE)
self.fields["med_num"] = forms.CharField(label = _("Trial bacth number"), required=True,validators=[validate_test] )
self.fields["asp_ent_dat"] = forms.DateField(
label = _("Entry date"),
required = True,
initial = datetime.datetime.now(),
)
self.fields["asp_ent_pro_pay"] = forms.ChoiceField(label = _("Country of treatment origin in case of entry"), widget=forms.Select, choices=PAYS)
self.fields["asp_ent_pro_sit"] = forms.ChoiceField(label = _("Processing source site in case of entry"), widget=forms.Select, choices=SITE_PROVENANCE)
def clean(self):
cleaned_data = super(EditForm, self).clean()
class Meta:
model = Entree
fields = ('asp_ent_loc','med_num','asp_ent_dat','asp_ent_pro_pay','asp_ent_pro_sit',)
def clean_med_num(self):
data = self.cleaned_data['med_num']
if len(data) != 3:
raise forms.ValidationError(_("Error on Batch number format (3 letters)"))
if not Medicament.objects.filter(med_num = data).exists():
raise ValidationError(_('Batch number does not exist'))
return data
def clean_asp_ent_dat(self):
data = self.cleaned_data['asp_ent_dat']
entrydate = datetime.datetime.strptime(str(data), "%Y-%m-%d")
currentdate = datetime.datetime.now()
if entrydate > currentdate:
raise forms.ValidationError(_("Please control entry date"))
return data
Your Validation function is not a model's method which means it doesn't accept the self. Your Validation func only accept value argument. In your scenario-
def validate_test(value): # remove self from here
if value == 'PPP':
raise ValidationError(
_("Test"),
code='Test',
params={},
)
I am trying to modify django-taggit to allow the same tag to be added by separate teams.
I have modified django-taggit Model so that it has user and team_id values added when a user adds a new tag to taggig_tag table. This also adds user and team_id values to taggit_taggeditems table.
The goal is to allow teams to edit or delete their own tags and that should not affect other teams, so different teams need to have their own separate sets of tags.
In my modified scenario, the tag name and team_id constitute a distinct tag. I expect i can test team_id or concatenate it to the tag name before django-taggit tests for distinct tags. But do not see where django-taggit does that.
Question: Where in the django-taggit code does it look for duplicate tag values?
`apps.py`
`forms.py`
`managers.py`
`models.py`
`utils.py`
`views.py`
My modified django-taggit Model code is below.
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import IntegrityError, models, router, transaction
from django.utils.text import slugify
from django.utils.translation import gettext, gettext_lazy as _
from django.conf import settings
### MODIFICATION: added django CRUM to get request user
from crum import get_current_user
try:
from unidecode import unidecode
except ImportError:
def unidecode(tag):
return tag
class TagBase(models.Model):
### MODIFICATION: added team and user to model, removed unique=True
name = models.CharField(verbose_name=_("Name"), max_length=100)
slug = models.SlugField(verbose_name=_("Slug"), max_length=100)
team_id = models.CharField(max_length=10, blank=False, null=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
on_delete=models.DO_NOTHING)
def __str__(self):
return self.name
def __gt__(self, other):
return self.name.lower() > other.name.lower()
def __lt__(self, other):
return self.name.lower() < other.name.lower()
class Meta:
abstract = True
def save(self, *args, **kwargs):
### MODIFICATION: added team and user to taggit_taggeditem model
### get request user with django CRUM get_current_user()
self.user = get_current_user()
self.team_id = get_current_user().team_id
if self._state.adding and not self.slug:
self.slug = self.slugify(self.name)
using = kwargs.get("using") or router.db_for_write(
type(self), instance=self
)
# Make sure we write to the same db for all attempted writes,
# with a multi-master setup, theoretically we could try to
# write and rollback on different DBs
kwargs["using"] = using
# Be oportunistic and try to save the tag, this should work for
# most cases ;)
### MODIFICATION: remove IntegrityError try/except for unique
which is removed
#try:
with transaction.atomic(using=using):
res = super().save(*args, **kwargs)
return res
#except IntegrityError:
# pass
### MODIFICATION: remove slugs create as no longer checking for
duplicate slugs
# Now try to find existing slugs with similar names
#slugs = set(
# self.__class__._default_manager.filter(
# slug__startswith=self.slug
# ).values_list("slug", flat=True)
#)
i = 1
#while True:
# slug = self.slugify(self.name, i)
# if slug not in slugs:
# self.slug = slug
# # We purposely ignore concurrecny issues here for now.
# # (That is, till we found a nice solution...)
# return super().save(*args, **kwargs)
# i += 1
while True:
slug = self.slugify(self.name, i)
#if slug not in slugs:
self.slug = slug
# We purposely ignore concurrecny issues here for now.
# (That is, till we found a nice solution...)
return super().save(*args, **kwargs)
i += 1
else:
return super().save(*args, **kwargs)
def slugify(self, tag, i=None):
slug = slugify(unidecode(tag))
if i is not None:
slug += "_%d" % i
return slug
class Tag(TagBase):
class Meta:
verbose_name = _("Tag")
verbose_name_plural = _("Tags")
app_label = "taggit"
class ItemBase(models.Model):
def __str__(self):
return gettext("%(object)s tagged with %(tag)s") % {
"object": self.content_object,
"tag": self.tag,
}
class Meta:
abstract = True
#classmethod
def tag_model(cls):
field = cls._meta.get_field("tag")
return field.remote_field.model
#classmethod
def tag_relname(cls):
field = cls._meta.get_field("tag")
return field.remote_field.related_name
#classmethod
def lookup_kwargs(cls, instance):
return {"content_object": instance}
class TaggedItemBase(ItemBase):
tag = models.ForeignKey(
Tag, related_name="%(app_label)s_%(class)s_items",
on_delete=models.CASCADE
)
class Meta:
abstract = True
#classmethod
def tags_for(cls, model, instance=None, **extra_filters):
kwargs = extra_filters or {}
if instance is not None:
kwargs.update({"%s__content_object" % cls.tag_relname():
instance})
return cls.tag_model().objects.filter(**kwargs)
kwargs.update({"%s__content_object__isnull" % cls.tag_relname():
False})
return cls.tag_model().objects.filter(**kwargs).distinct()
class CommonGenericTaggedItemBase(ItemBase):
content_type = models.ForeignKey(
ContentType,
on_delete=models.CASCADE,
verbose_name=_("Content type"),
related_name="%(app_label)s_%(class)s_tagged_items",
)
content_object = GenericForeignKey()
### MODIFICATION: added team and user to taggit_taggeditem model
team_id = models.CharField(max_length=10, blank=False, null=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
on_delete=models.DO_NOTHING)
class Meta:
abstract = True
#classmethod
def lookup_kwargs(cls, instance):
return {
"object_id": instance.pk,
"content_type": ContentType.objects.get_for_model(instance),
### MODIFICATION: added team and user to taggit_taggeditem model
"user": get_current_user(),
"team_id": get_current_user().team_id,
}
#classmethod
def tags_for(cls, model, instance=None, **extra_filters):
tag_relname = cls.tag_relname()
kwargs = {
"%s__content_type__app_label" % tag_relname:
model._meta.app_label,
"%s__content_type__model" % tag_relname: model._meta.model_name,
}
if instance is not None:
kwargs["%s__object_id" % tag_relname] = instance.pk
if extra_filters:
kwargs.update(extra_filters)
return cls.tag_model().objects.filter(**kwargs).distinct()
class GenericTaggedItemBase(CommonGenericTaggedItemBase):
object_id = models.IntegerField(verbose_name=_("Object id"),
db_index=True)
class Meta:
abstract = True
class GenericUUIDTaggedItemBase(CommonGenericTaggedItemBase):
object_id = models.UUIDField(verbose_name=_("Object id"), db_index=True)
class Meta:
abstract = True
class TaggedItem(GenericTaggedItemBase, TaggedItemBase):
class Meta:
verbose_name = _("Tagged Item")
verbose_name_plural = _("Tagged Items")
app_label = "taggit"
### MODIFICATION: added team_id and user to taggit_taggeditems table
constraints
index_together = [["content_type", "object_id", "team_id", "user"]]
unique_together = [["content_type", "object_id", "tag", "team_id",
"user"]]
Just quickly to summarize I have an app, which allows clubs to sign up and carry out different tasks. One of the features is a scheduling / rosters. Currently I have a form to add times to the roster. The club pages work off a session key based on the initially selected club.
My form consists of:
club_id - which I have hidden and initialized based on the logged in user.
pitch_id - which is currently displaying all pitches associated to all clubs but I need this to only show pitches based on the foreign key for the club_id
Would appreciate any help.
form.py
from django import forms
from clubkit.roster.models import RosterId
import datetime
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
class RosterForm(forms.ModelForm):
class Meta():
model = RosterId
fields = ('club_id', 'pitch_id', 'team_id', 'date',
'start_time', 'finish_time', 'reoccuring_event',)
widgets = {
'date': forms.DateInput(attrs={'id': 'datepicker'})
}
def clean_date(self):
date = self.clean_date['date']
if date < datetime.date.today():
raise ValidationError(_('Date cannot be in the past.'))
return date
def __init__(self, *args, **kwargs):
super(RosterForm, self).__init__(*args, **kwargs)
self.fields['club_id'].widget = forms.HiddenInput()
models.py for pitch
class Pitch(models.Model):
club_id = models.ForeignKey(ClubInfo, on_delete=models.CASCADE, related_name="pitches")
pitch_name = models.CharField(max_length=30)
PITCH_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
PITCH_TYPE = (
('1', 'Outdoor'),
('2', 'Indoor'),
)
pitch_size = models.CharField(max_length=1, choices=PITCH_SIZES)
pitch_type = models.CharField(max_length=1, choices=PITCH_TYPE)
open_time = models.TimeField(default='09:00')
close_time = models.TimeField(default='22:00')
RENT_TYPE = (
('0', 'Not Available To Rent'),
('1', 'Available To Rent'),
)
rental = models.CharField(max_length=1, choices=RENT_TYPE)
rental_price = models.DecimalField(default=0.00, max_digits=6, decimal_places=2)
max_people = models.IntegerField(null=True)
def __str__(self):
return self.pitch_name
models.py for roster
from django.db import models
from clubkit.clubs.models import ClubInfo, Pitch, Team
# Model to store roster information
class RosterId(models.Model):
club_id = models.ForeignKey(ClubInfo, on_delete=models.CASCADE)
pitch_id = models.ForeignKey(Pitch, on_delete=models.CASCADE)
team_id = models.ForeignKey(Team, on_delete=models.CASCADE)
date = models.DateField(max_length=8)
start_time = models.TimeField(default='')
finish_time = models.TimeField(default='')
reoccuring_event = models.BooleanField(default=False)
views.py
class ClubRoster(APIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'roster.html'
# Get method to retrieve current roster information and form
def get(self, request):
if request.user.is_authenticated:
club_pk = request.session.get('pk')
club_info = ClubInfo.objects.filter(user=request.user).first()
reoccuring_event = RosterId.objects.filter(reoccuring_event=True, club_id=club_pk)
inital_data = {
'club_id': club_info,
}
form = RosterForm(initial=inital_data)
roster = RosterId.objects.filter(club_id=club_pk)
return Response({'form': form,
'roster': roster,
'club_pk': club_pk,
'reoccuring_event': reoccuring_event
})
Sounds like you're looking for some manipulation of the form field query. Maybe this answer will provide further help.
The result could look like this.
from django import forms
from clubkit.roster.models import RosterId, Pitch
import datetime
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
class RosterForm(forms.ModelForm):
class Meta():
model = RosterId
fields = ('club_id', 'pitch_id', 'team_id', 'date',
'start_time', 'finish_time', 'reoccuring_event',)
widgets = {
'date': forms.DateInput(attrs={'id': 'datepicker'})
}
def clean_date(self):
date = self.clean_date['date']
if date < datetime.date.today():
raise ValidationError(_('Date cannot be in the past.'))
return date
def __init__(self, *args, **kwargs):
super(RosterForm, self).__init__(*args, **kwargs)
self.fields['club_id'].widget = forms.HiddenInput()
instance = kwargs.get('instance', None)
if instance:
self.fields['pitch_id'].queryset = Roster.objects.filter(club_id=instance.club_id)
Ah well or adjust instance to your 'initial'.
I have the following models:
from django.db import models
MNL = 50
MCL = 5
class Continent(models.Model):
"""
Fields
"""
name = models.CharField("name", max_length=MNL, unique=True)
code = models.CharField("code", max_length=MCL, default="", unique=True)
class Meta:
ordering = ['name']
"""
Methods
"""
def __str__(self):
return "%s, %s" % (self.name, self.code)
class Country(models.Model):
"""
Fields
"""
name = models.CharField("name", max_length=MNL, unique=True)
capital = models.CharField("capital", max_length=MNL)
code = models.CharField("code", max_length=MCL, default="", unique=True)
population = models.PositiveIntegerField("population")
area = models.PositiveIntegerField("area")
continent = models.ForeignKey(Continent, on_delete=models.CASCADE,
related_name="countries")
class Meta:
ordering = ['name']
"""
Methods
"""
def __str__(self):
return "%s, %s" % (self.name, self.code)
I need to be able to retrieve 2 things in JSON(P):
individual Country's capital, population and area fields in the form {"area":<area>,"population":<population>,"capital":<capital_name>} and
in the case of a Continent, all of the countries in that continent in the form {"code1":"name1", "code2":"name2",...}
I've tried implementing the following views to achieve this:
from django.http import HttpResponse, Http404, JsonResponse
from django.forms.models import model_to_dict
import json
from .models import Continent, Country
def continent_json(request, continent_code):
""" Write your answer in 7.2 here. """
try:
print("CONTINENT QuerySet: ", Continent.objects.filter(
code__exact=continent_code).values("countries"))
continent_data = json.dumps( list(Continent.objects.filter(
code__exact=continent_code).values("countries") ) )
print("CONTINENT JSON: ",continent_data)
except Continent.DoesNotExist:
raise Http404("Requested continent does not exist.")
# If JSONP
if "callback" in request.GET:
continent_data = "{}({})".format(
request.GET["callback"],
continent_data
)
return HttpResponse(continent_data)
# Normal JSON
return HttpResponse(continent_data, content_type="application/json")
def country_json(request, continent_code, country_code):
""" Write your answer in 7.2 here. """
try:
#print("COUNTRY_OBJECT: "Country.objects.filter(code__exact=country_code).values())
print("MODEL_LIST: ",list(Country.objects.filter(code__exact=country_code).values("capital","population","area")))
country_data = json.dumps( list(Country.objects.filter(
code__exact=country_code).values("code","name") ) )
print("COUNTRY DATA: ", country_data)
except Country.DoesNotExist:
raise Http404("Requested country does not exist.")
# If JSONP
if "callback" in request.GET:
country_data = "{}({})".format(
request.GET["callback"],
country_data
)
return HttpResponse(country_data)
# Normal JSON
return HttpResponse(country_data, content_type="application/json")
However, this is not producing the results I want: the data is not actually coming back as JSON(P), but as either a dict or a list. This is a lot of code to shift through, but I'm at my wits end here.
What am I doing wrong?
From what you explained on chat:
You need to change your views to something like this
country_data = json.dumps(dict(Country.objects.filter(
code__exact=country_code).values("code","name")[0])))
and for continent view:
continent = Continent.objects.get(code__exact=continent_code)
country_data = json.dumps(dict(continent.countries.values_list('code', 'name')))