I'm attempting to create a voting system where the score of a given post is separate from the type of votes placed by users. In the event that a user deletes a profile, a given score should not increment/decrement due to their vote being deleted. Therefore scores are only updated by using an F('score') + 1 or F('score') - 1 expression.
Within Vote.save(), I'm trying to implement this, yet the Question.score field doesn't update when the Vote is created. How can I get the test to pass where the score in the question goes from 0 to 1? django.db.models.F is in fact being imported into the module but it's not displayed here.
class TestQuestionScoreUpVote(TestCase):
'''Verify that a Question's score increments by one point
when the Vote is an "up" vote.'''
#classmethod
def setUpTestData(cls):
tag1 = Tag.objects.create(name="Tag1")
user = get_user_model().objects.create_user("TestUser")
profile = Profile.objects.create(user=user)
cls.question = Question.objects.create(
title="Question__001",
body="Content of Question 001",
profile=profile
)
cls.question.tags.add(tag1)
user_vote = Vote.objects.create(
profile=profile, type="upvote", content_object=cls.question
)
def test_new_user_vote_on_question(self):
self.assertEqual(self.question.score, 1)
class Post(Model):
body = TextField()
date = DateField(default=date.today)
comment = ForeignKey('Comment', on_delete=CASCADE, null=True)
profile = ForeignKey(
'authors.Profile', on_delete=SET_NULL, null=True,
related_name='%(class)ss',
related_query_name="%(class)s"
)
vote = GenericRelation(
'Vote', related_query_name="%(class)s"
)
score = IntegerField(default=0)
class Meta:
abstract = True
class Question(Post):
title = CharField(max_length=75)
tags = ManyToManyField(
'Tag', related_name="questions", related_query_name="question"
)
views = IntegerField(default=0)
objects = Manager()
postings = QuestionSearchManager()
class Meta:
db_table = "question"
ordering = ["-score" , "-date"]
def __repr__(self):
return f"{self.__class__.__name__}(title={self.title})"
class Vote(Model):
profile = ForeignKey(
'authors.Profile', on_delete=SET_NULL, null=True,
related_name="votes"
)
type = CharField(max_length=7)
content_type = ForeignKey(ContentType, on_delete=CASCADE)
object_id = PositiveIntegerField()
content_object = GenericForeignKey()
def save(self, *args, **kwargs):
post = ContentType.objects.get_for_id(
self.content_type_id
).get_object_for_this_type(id=self.object_id)
if self.type == "upvote":
post.score = F("score") + 1
else:
post.score = F("score") - 1
post.refresh_from_db()
super().save(*args, **kwargs)
You are forgetting to call post.save() after the object change:
def save(self, *args, **kwargs):
post = ContentType.objects.get_for_id(
self.content_type_id
).get_object_for_this_type(id=self.object_id)
if self.type == "upvote":
post.score = F("score") + 1
else:
post.score = F("score") - 1
post.save() # <- HERE
post.refresh_from_db()
super().save(*args, **kwargs)
Related
I am building an API for a game and need to select a random game round for a randomly chosen resource.
Choosing the random resource works. What I am trying to do now, in order to coordinate players is to filter the game rounds by the resource that has been chosen randomly and then return a random game round.
The code below shows what I have tried so far, namely to access the resource for which a game round has been played over the method with the #property decorator.
models.py
class Resource(models.Model):
id = models.PositiveIntegerField(null=False, primary_key=True)
hash_id = models.CharField(max_length=256)
creators = models.ManyToManyField(Creator)
titles = models.ManyToManyField(Title)
created_start = models.DateField(null=True)
created_end = models.DateField(null=True)
location = models.CharField(max_length=512, null=True)
institution_source = models.CharField(max_length=512, blank=True)
institution = models.CharField(max_length=512, blank=True)
origin = models.URLField(max_length=256, null=True)
enabled = models.BooleanField(default=True)
media_type = models.CharField(max_length=256, default='picture')
objects = models.Manager()
def __str__(self):
return self.hash_id or ''
#property
def tags(self):
tags = self.taggings.values('tag').annotate(count=Count('tag'))
return tags.values('tag_id', 'tag__name', 'tag__language', 'count')
class Gameround(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True)
gamesession = models.ForeignKey(Gamesession, on_delete=models.CASCADE)
created = models.DateTimeField(editable=False)
score = models.PositiveIntegerField(default=0)
objects = models.Manager()
def save(self, *args, **kwargs):
if not self.id:
self.created = timezone.now()
return super().save(*args, **kwargs)
def create(self):
pass
#property
def tags(self):
tags = self.taggings.values('tag')
return tags.values('tag_id', 'tag__name', 'tag__language', 'resource_id')
class Tagging(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True)
gameround = models.ForeignKey(Gameround, on_delete=models.CASCADE, related_name='taggings')
resource = models.ForeignKey(Resource, on_delete=models.CASCADE, related_name='taggings')
tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
created = models.DateTimeField(editable=False)
score = models.PositiveIntegerField(default=0)
origin = models.URLField(max_length=256, blank=True, default='')
objects = models.Manager()
def create(self, tag):
tagging = self.create(tag=tag)
def __str__(self):
return str(self.tag) or ''
def save(self, *args, **kwargs):
if not self.id:
self.created = timezone.now()
return super().save(*args, **kwargs)
I am then serialising the game round like this:
*serializers.py
class GameroundSerializer(serializers.ModelSerializer):
gamesession = GamesessionSerializer(read_only=True)
user = CustomUserSerializer(read_only=True)
tags_to_compare = serializers.SerializerMethodField('get_tags_to_compare')
class Meta:
model = Gameround
fields = ['id', 'user', 'gamesession', 'created', 'score', 'tags_to_compare']
def get_tags_to_compare(self, round):
taggings = round.tags
return taggings
def to_representation(self, data):
data = super().to_representation(data)
return data
In the view I have a get request which first chooses a random resource, then I am trying to filter the game rounds to only include the ones where this resource was involved and then I am trying to retrieve a random game round.
views.py
class GameroundView(APIView):
"""
API endpoint for game rounds
"""
def get(self, request, *args, **kwargs):
controller = GameViewController()
random_resource = controller.get_resource()
gameround = Gameround.objects.all().filter(taggings__resource_id=random_resource).order_by("?").first()
gameround_serializer = GameroundSerializer(gameround)
return Response(gameround_serializer.data)
This does not seem to be working since I keep getting the error "The response content must be rendered before it can be iterated over."
I have also tried creating an id variable and passing it on as the resource_id like this:
# id of the random Resource for the game round
random_resource_id = random_resource.get(random_resource.id)
gameround = Gameround.objects.all().filter(taggings__resource_id=random_resource_id).order_by("?").first()
This leads to the 'Response' object has no attribute 'id' error when I reload the page in the browser.
Does anyone know how I can solve this issue?
Thank you in advance!
Below also the Gameviewcontroller that is inside the views.py file
class GameViewController:
def get_random_object(self, MyModel):
random_object = None
object_count = MyModel.objects.all().count() + 1
while not MyModel.objects.all().filter(id=random_object).exists():
for obj in range(object_count):
n = random.randint(1, object_count)
if MyModel.objects.all().filter(id=n).exists():
random_object = n
return random_object
def get_random_id(self, MyModel):
"""Method for large querysets"""
random_object = None
object_count = MyModel.objects.all().count() + 1
for obj in range(object_count):
n = random.randint(1, object_count)
while not MyModel.objects.all().filter(id=n).exists():
alt = random.randint(1, object_count)
if MyModel.objects.all().filter(id=alt).exists():
random_object = n
return random_object
def get_resource(self):
random_resource_id = self.get_random_object(Resource)
current_resource = Resource.objects.all().filter(id=random_resource_id)
resource_serializer = ResourceSerializer(current_resource, many=True)
data = {'resource': resource_serializer.data}
return Response(data, status=status.HTTP_200_OK)
def get_gameround_matching_resource(self, random_resource):
"""Checks if a previously played game round exists for a randomly chosen resource"""
current_gameround = Gameround.objects.all().filter(ressource__in=random_resource).order_by('?').first()
if current_gameround.exists():
gameround_serializer = GameroundSerializer(current_gameround)
data = {'gameround': gameround_serializer.data}
return Response(data, status=status.HTTP_200_OK)
def timer(self, start):
"""Start a new timer as soon as a gameround has been selected"""
start_time = start
elapsed_time = None
if start_time is not None:
start_time = time.perf_counter()
"""Stop the timer, and return the elapsed time"""
if start_time is None:
elapsed_time = time.perf_counter() - start_time
return elapsed_time
def get_gamesession(self):
"""Retrieves a randomly chosen gamesession object"""
current_gamesession = Gamesession.objects.all().order_by('?').first()
gamesession_serializer = GamesessionSerializer(current_gamesession)
data = {'gameround': gamesession_serializer.data}
return Response(data, status=status.HTTP_200_OK)
I finally managed to solve it by rewriting the get request like this:
def get(self, request, *args, **kwargs):
random_resource = Resource.objects.all().order_by('?').first()
resource_serializer = ResourceSerializer(random_resource) # Response is a serialized JSON object
random_resource_id = random_resource.id # id of the random Resource for the game round
gameround = Gameround.objects.all().filter(taggings__resource_id=random_resource_id).order_by("?").first()
gameround_serializer = GameroundSerializer(gameround)
return Response({
'resource to coordinate': resource_serializer.data,
'gameround': gameround_serializer.data,
})
I've been stuck on this problem for hours, looking through Django documentation and adjusting my code hasn't helped.
I have manually coded a HTML form (for styling reasons). Everything works fine until I try to edit an object via this form.
I'm using a standard UpdateView with no additions but for some reason the form is not populating the data in the object.
class RPLogbookEditEntry(LoginRequiredMixin, UpdateView):
login_url = 'ihq-login'
template_name = 'internethq/ihq-rplogbookform.html'
model = RPLogbookEntry
fields = ['entry_date', 'rpa_code', 'rpa_icao_code','rpa_registration', 'user_role', 'crew', 'crew_role', 'launch_location', 'recovery_location',
'remarks', 'vlos', 'evlos1', 'evlos2', 'bvlos', 'ft_day', 'ft_night', 'remarks', 'no_of_landings_day', 'no_of_landings_night']
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.rpa = RPA.objects.get(rpa_code=form.cleaned_data['rpa_code'])
return super().form_valid(form)
def form_invalid(self, form):
return super().form_invalid(form)
def get_success_url(self):
return reverse('ihq-rplogbook-list') + '?month=' + str(self.object.entry_date.month) + '&year=' + str(self.object.entry_date.year)
models.py
class RPLogbookEntry(models.Model):
## Date
entry_date = models.DateField()
## RPA Details
rpa_obj = RPA.objects.only('rpa_code')
RPA_CODE_CHOICES = []
for vars in rpa_obj:
RPA_CODE_CHOICES.append((vars.rpa_code, vars.rpa_code))
rpa_code = models.CharField(choices=RPA_CODE_CHOICES, max_length=20)
rpa_icao_code = models.CharField(max_length=15)
rpa_registration = models.CharField(max_length=30)
rpa = models.ForeignKey(RPA, on_delete=models.CASCADE)
## Crew Details
user = models.ForeignKey(User, on_delete=models.CASCADE)
USER_ROLE_CHOICES = [
('RPIC', 'Remote Pilot-In-Command'),
('RP', 'Remote Pilot'),
('SPOT', 'Spotter'),
('CAMOP', 'Camera operator'),
]
user_role = models.CharField(
max_length=5,
choices=USER_ROLE_CHOICES,
)
crew = models.CharField(max_length=128, blank=True)
crew_role = models.CharField(
max_length=5,
choices=USER_ROLE_CHOICES,
blank = True
)
## Flight Details
launch_location = models.CharField(max_length=128)
recovery_location = models.CharField(max_length=128)
remarks = models.CharField(max_length=256, blank=True)
vlos = models.BooleanField(default=True)
evlos1 = models.BooleanField(default=False)
evlos2 = models.BooleanField(default=False)
bvlos = models.BooleanField(default=False)
## Flight Time
ft_day = FlightTimeFieldInt("Day flight time", blank=True, null=True,)
ft_night = FlightTimeFieldInt("Night flight time", blank=True, null=True,)
def _ft_total(self):
return int(self.ft_day + self.ft_night)
ft_total = property(_ft_total)
## Category of mission or flight
MISSION_CATEGORY_CHOICES = [
('INS', 'Inspection'),
('MAP', 'Mapping'),
('PHO', 'Photography'),
('VID', 'Videography'),
('PRI', 'Private flying'),
('TRA', 'Training'),
]
mission_category = models.CharField(
max_length=3,
choices=MISSION_CATEGORY_CHOICES,
blank=True,
)
## Landings & Autolands
no_of_landings_day = models.IntegerField("Number of landings", blank=True, default=0,)
no_of_landings_night = models.IntegerField("Number of auto landings", blank=True, default=0,)
def get_absolute_url(self):
return reverse('ihq-rplogbook-list')
def clean(self):
if self.no_of_landings_day is None and self.no_of_landings_night is None:
raise ValidationError({'no_of_landings_day':_('You must have landed at least once!')})
if self.no_of_landings_night is None:
if self.no_of_landings_day >= 1:
self.no_of_landings_night = 0
return self.no_of_landings_night
if self.no_of_landings_day is None:
if self.no_of_landings_night >= 1:
self.no_of_landings_day = 0
return self.no_of_landings_day
def clean_fields(self, exclude=None):
validation_error_dict = {}
p = re.compile('^[0-9]+:([0-5][0-9])')
if self.ft_day !='' and p.match(self.ft_day) is None:
validation_error_dict['ft_day'] = ValidationError(_('Flight duration must be in HH:MM format.'))
if self.ft_night !='' and p.match(self.ft_night) is None:
validation_error_dict['ft_night'] = ValidationError(_('regex night wrong'))
if self.ft_day =='' and self.ft_night =='':
validation_error_dict['ft_day'] = ValidationError(_('Fill in at least one flying time duration.'))
if (self.vlos or self.evlos1 or self.evlos2 or self.bvlos) is False:
validation_error_dict['vlos'] = ValidationError(_('Select one LOS category.'))
if (self.vlos or self.evlos1 or self.evlos2 or self.bvlos) is True:
counter = 0
for x in [self.vlos, self.evlos1, self.evlos2, self.bvlos]:
if x == True:
counter += 1
if counter > 1:
validation_error_dict['vlos'] = ValidationError(_('Selecting more than one LOS category is not allowed.'))
# if self.no_of_landings is None and self.no_of_auto_landings is None:
# validation_error_dict['no_of_landings'] = ValidationError(_('You must have landed at least once!'))
if validation_error_dict:
raise ValidationError(validation_error_dict)
Basically my question is - shouldn't this UpdateView populate my form automatically?
Very silly mistake on my part.
I was using the same HTML template to add and edit these records, which had the {{form.cleaned_data.field_name}} in the <input value="..> tag. This works for retaining data when adding a new record, such as an invalid form, but won't show up when editing an object.
The fix was to create a new template (identical page), but fill the <input value="... with the necessary object value template tag i.e. {{object.field_name}}.
I have a webapp where we can create communities with django as a backend, but when i try to send a POST to create a community, I get:
community_configuration: ["Incorrect type. Expected pk value, received str."]
My POST:
{
title: knkn kn .k jbjnmn,
logo: [object File],
is_active: true,
description: test,
welcome_message: Welcome message,
org_id: 114,
community_configuration: About us,Community news,FAQs,Supporters,Resources,
}
Here are my serializers:
class MicroConfigurationSerializer(serializers.ModelSerializer):
class Meta:
model = MicroConfiguration
fields = [
'name',
]
def to_representation(self, instance):
return instance.name
class CommunityConfigurationSerializer(serializers.ModelSerializer):
micro_configurations = MicroConfigurationSerializer(many=True, read_only=True)
class Meta:
model = CommunityConfiguration
fields = '__all__'
class CommunitySerializer(serializers.ModelSerializer):
logo = serializers.SerializerMethodField()
organisation = serializers.SerializerMethodField()
community_configuration = CommunityConfigurationSerializer()
class Meta:
model = Community
fields = (
...
etcetc
...
)
Heres my model:
class MicroConfiguration(core_models.BaseModel):
name = models.CharField(max_length=32, unique=True)
permissions = models.ManyToManyField(Permission)
def __str__(self):
return self.name
class CommunityConfiguration(core_models.BaseModel):
name = models.CharField(max_length=32)
micro_configurations = models.ManyToManyField(MicroConfiguration)
permission_group = models.ForeignKey(
Group,
on_delete=models.SET_NULL,
null=True
)
def __str__(self):
return self.name
class Community(core_models.BaseModel):
accounts = models.ManyToManyField(
settings.AUTH_USER_MODEL,
through='associates.AccountCommunity',
through_fields=('community', 'account')
)
goals = models.ManyToManyField(Goal, through='associates.CommunityGoal')
type = models.ManyToManyField(CommunityType, through='CommunityAttribute')
communities = models.ManyToManyField(
'self',
through='associates.CommunityCommunity',
symmetrical=False,
)
crossposts = models.ManyToManyField(
Action,
through='activities.CommunityCrosspost',
through_fields=('community', 'action'),
related_name='communities',
)
title = models.CharField(max_length=64)
logo = models.ImageField(null=True, blank=True)
intro_media = models.ForeignKey(
'associates.MediaStore',
null=True,
on_delete=models.SET_NULL
)
website_url = models.CharField(max_length=256, blank=True)
is_invite_only = models.BooleanField(default=False)
description = models.TextField(blank=True)
intro_message = models.TextField(blank=True)
welcome_message = models.TextField(blank=True)
signup_autojoin = models.BooleanField(default=False)
community_configuration = models.ForeignKey(
CommunityConfiguration,
default=1,
null=True,
on_delete=models.SET_NULL,
help_text='Do not edit directly, create a new custom config instead, \
as it is reference by difference community.',
)
objects = models.Manager()
associates = AssociationManager()
#property
def url(self):
return reverse(
'communities:detail',
kwargs={
'id': self.id,
}
)
class Meta:
verbose_name = 'Community'
verbose_name_plural = 'Communities'
def __str__(self):
return self.title
Here's my views:
class CommunityCreateAPIView(CreateAPIView):
serializer_class = CommunityCreateSerializer
def create(self, request, **kwargs):
organisation_id = request.data.get('org_id')
micro_configurations_qd = request.data.copy()
micro_configurations = micro_configurations_qd.pop('community_configuration', False)
if micro_configurations:
micro_configurations = micro_configurations[0].split(',')
user = request.user
data = {}
permitted = AccountOrganisation.objects.filter(
account=user,
organisation_id=organisation_id,
is_active=True,
association__permissions__codename='add_community',
).exists()
if not permitted and not request.user.is_superuser:
data['message'] = 'Action not allowed'
return Response(
data=data,
status=status.HTTP_400_BAD_REQUEST,
)
response = super().create(request, **kwargs)
if response.status_code == 400:
data['message'] = 'Failed to update community'
return Response(
data=data,
status=status.HTTP_400_BAD_REQUEST,
)
community_id = response.data.get('id')
OrganisationCommunity.objects.create(
community_id=community_id,
organisation_id=organisation_id,
)
association = Group.objects.get(name='Community Admin')
AccountCommunity.objects.create(
community_id=community_id,
account=user,
association=association,
)
if micro_configurations:
com_config_qs = CommunityConfiguration.objects.filter(
micro_configurations__name__in=micro_configurations
).annotate(
num_micro_config=Count('micro_configurations__name')
).filter(
num_micro_config=len(micro_configurations)
)
community_configuration = None
if micro_configurations:
for com_config in com_config_qs:
micro_config_count = com_config.micro_configurations.count()
if micro_config_count == len(micro_configurations):
community_configuration = com_config
break
if community_configuration:
Community.objects.filter(
pk=community_id
).update(
community_configuration=community_configuration
)
elif micro_configurations:
micro_qs = MicroConfiguration.objects.filter(
name__in=micro_configurations
)
community_config = CommunityConfiguration.objects.create(
name='Custom'
)
community_config.micro_configurations.set(micro_qs)
community_config.save()
Community.objects.filter(
pk=community_id
).update(
community_configuration=community_config
)
return response
I've been stuck with this for hours, any help?
Your serializer expects a pk (or id) of the community_configuration instance.
basically you have it set up so that you need to create a community_configuration entry first, then fetch the id of the new created entry and use that id when creating your community.
If you want to have the community_configuration instance to be created while creating the community then an option to do that would be to override the create method in the serializer and extract the community_configuration data then create a new entry for that model and use it, something like this
class CommunitySerializer(serializers.ModelSerializer):
# .. put your serializer code here
def create(self, validated_data):
community_configuration= validated_data.pop('community_configuration')
config_instance, created = CommunityConfiguration.objects.get_or_create(<community_configuration>) # modify this however you need to create the CommunityConfiguration instance
community_instance = community.objects.create(**validated_data, community_configuration=config_instance)
return community_instance
you will need to probably modify the code for it to follow your needs.
I didn't read all of your models, not sure how you are gonna create the nested models from the input value but you get the point.
I am working on a set of product / category relationships in a Django application.
A product can belong to any category and needs to be ordered within that category, I am trying to do this using a Many to Many relationship with a "through=" option.
When a POST request is made via Ajax it takes the form of b'ordered_products=4&ordered_products=5', I received an error straight after the forms __init__ call that "5 is not one of the available choices" where 5 is the valid id of anOrderedCategoryManagedProduct object.
models.py
class Category(models.Model):
name = models.CharField(max_length=128)
slug = models.SlugField(max_length=128, unique=True)
class Product(models.Model):
name = models.CharField(max_length=128)
category_management = models.ManyToManyField(
Category,
related_name="category_managed_products",
through="OrderedCategoryManagedProduct",
blank=True,
verbose_name="Category Management",
)
class OrderedCategoryManagedProduct(SortableModel):
category = models.ForeignKey(
Category, on_delete=models.CASCADE, related_name="cm_products"
)
product = models.ForeignKey(
Product, on_delete=models.CASCADE, related_name="cm_categories"
)
class Meta:
ordering = ["sort_order"]
def get_ordering_queryset(self):
return self.product.category_management()
class SortableModel(models.Model):
sort_order = models.IntegerField(db_index=True, null=True)
class Meta:
abstract = True
views.py
# POST = <QueryDict: {'ordered_products': [5, 4]}>
#staff_member_required
#permission_required("menu.manage_menus")
def ajax_reorder_menu_items(request, category_pk):
category = get_object_or_404(Category, pk=category_pk)
form = ReorderCategoryProductsForm(request.POST, instance=category)
status = 200
ctx = {}
if form.is_valid():
form.save()
elif form.errors:
status = 400
ctx = {"error": form.errors}
return JsonResponse(ctx, status=status)
forms.py
class ReorderCategoryProductsForm(forms.ModelForm):
ordered_products = OrderedModelMultipleChoiceField(
queryset=OrderedCategoryManagedProduct.objects.none()
)
class Meta:
model = Category
fields = ["id"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance:
self.fields["ordered_products"].queryset = self.instance.cm_products.all()
pass
def save(self):
for sort_order, category in enumerate(self.cleaned_data["ordered_products"]):
category.cm_products.sort_order = sort_order
category.save()
return self.instance
class OrderedModelMultipleChoiceField(forms.ModelMultipleChoiceField):
def clean(self, value):
qs = super().clean(value)
keys = list(map(int, value))
return sorted(qs, key=lambda v: keys.index(v.pk))
Big thanks to the #django IRC channel for the help on this, if anyone suffers the same problem the correct code was as follows:
class ReorderCategoryProductsForm(forms.ModelForm):
ordered_products = OrderedModelMultipleChoiceField(
queryset=OrderedCategoryManagedProduct.objects.all()
)
class Meta:
model = Category
fields = ["id"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance:
self.fields["ordered_products"].queryset = self.instance.cm_products.all()
pass
def save(self):
for sort_order, ocmp in enumerate(self.cleaned_data["ordered_products"]):
ocmp.sort_order = sort_order
ocmp.save()
return self.instance
I have 3 primary models. Questionnaire model or question set contains set of questions. All user response are stored in answer.
Now I have to generate a formset which will store answer of all questions in a questionnaire set. How can I do it in django. So far I have manged to do it by displaying single question at once from a given questionnaire and store the response. My problem is that based on questiontype use two different modelform (MultipleChoiceAnswerForm,DescriptiveChoiceAnswerForm) and validate them based on the formtype. How can I use it in formset.
I am beginner in django and any help is appreciated.
My Code:
#Models.py
class Question(models.Model):
statement = models.CharField(max_length=255)
question_type = models.CharField(max_length=20, choices=get_qtypes())
remarks = models.CharField(max_length=200, null=True, blank=True)
def __unicode__(self):
return '%s'%(self.statement)
class Questionnaire(models.Model):
title = models.CharField(max_length=255)
questionaire_type = models.CharField(max_length=20,choices=QUESTIONNAIRETYPE)
context = models.ForeignKey(QuestionContext)
questions = models.ManyToManyField(Question)
timestamp = models.DateTimeField(auto_now=True)
tathya_user = models.ForeignKey(User)
def __unicode__(self):
return '%s'%(self.title)
class Answer(models.Model):
question = models.ForeignKey(Question)
person = models.ForeignKey(Person)
course = models.ForeignKey(Course)
teacher=models.ForeignKey(Person, null=True, blank=True, default = None)
questionaire = models.ForeignKey(Questionnaire)
statement = models.CharField(max_length=255)
def get_label(self):
return '%s'%(self.question.statement)
def get_choices(self):
return get_questionchoices(self.question.question_type)
class DescriptiveAnswerForm(ModelForm):
def __init__(self, *args, **kwargs):
super(DescriptiveAnswerForm, self).__init__(*args, **kwargs)
if kwargs.has_key('instance'):
self.fields['statement'].label = kwargs['instance'].get_label()
statement = forms.CharField(widget=forms.Textarea())
class Meta:
model = Answer
exclude=('question','person','course','teacher','questionaire')
class MultipleChoiceAnswerForm(ModelForm):
statement = forms.ChoiceField(widget=forms.RadioSelect(choices=EMPTY,attrs={'class': 'allradio',}))
def __init__(self, *args, **kwargs):
super(MultipleChoiceAnswerForm, self).__init__(*args, **kwargs)
if kwargs.has_key('instance'):
self.fields['statement'].label = kwargs['instance'].get_label()
self.fields['statement'].choices = kwargs['instance'].get_choices()
class Meta:
model = Answer
exclude=('question','person','course','teacher','questionaire')
###################################################################
#view.py
#login_required
def content_feedback_view_old(request,course_code):
#do validation and other jobs
questionnaire = get_questionnaire(some_params_like_coursecode)
if request.method == 'POST':
r_answer = Answer()
r_answer.question = Question.objects.get(id=request.session['question'])
r_answer.person = student
r_answer.course = course
r_answer.questionaire = questionnaire
r_answer.tathya_user = User.objects.get(id=request.user.pk)
rformtype = request.POST['formtype']
if rformtype == 'MCQ':
rform = MultipleChoiceAnswerForm(request.POST, instance=r_answer)
else:
rform = DescriptiveAnswerForm(request.POST, instance=r_answer)
if rform.is_valid():
rform.save()
else:
#return HttpResponse(printerror("Some problem occurred!"))
errortext = "You need to provide an input!"
questions = questionnaire.questions.all()
allquestions = questions.count()
tot_q = 0
formtype = ""
answered = 0
for question in questions:
try:
answer=Answer.objects.get(question=question,person=student,course=course,questionaire=questionnaire)
answered += 1
except:
answer = Answer()
answer.question = question
answer.person = student
answer.course = course
answer.questionaire = questionnaire
answer.tathya_user = User.objects.get(id=request.user.pk)
request.session['question']=question.id
tot_q = tot_q + 1;
if get_questiontype(question.question_type)=='MCQ':
formtype="MCQ"
form=MultipleChoiceAnswerForm(instance=answer)
else:
formtype="DESC"
form=DescriptiveAnswerForm(instance=answer)
break
if tot_q>0:
data_dict['FeedbackFormType']=formtype
data_dict['FeedbackForm']=form
data_dict['pagetitle']=context.description
data_dict['coursecode']=course.course_code
data_dict['feedbacktitle']="Content Feedback for "+course.fullname
data_dict['Completeness'] = (answered/allquestions)*100
data_dict['error']=errortext
else:
return HttpResponse(printerror("Thanks! You've answered all the questions!<br>Continue with the teaching feedback."))
req_context = RequestContext(request)
return render_to_response('view.html', data_dict, req_context)
Simple answer: only use on single AnswerForm and let it manage which kind of field and widget it should use, ie:
class AnswerForm(ModelForm):
def __init__(self, *args, **kwargs):
super(AnswerForm, self).__init__(*args, **kwargs)
instance = self.instance
if instance.question.question_type == 'MCQ':
self.fields["statement"] = forms.ChoiceField(
choices=instance.get_choices(),
widget=forms.RadioSelect(attrs={'class': 'allradio',})
)
else:
self.fields["statement"] = forms.CharField(
widget=forms.Textarea()
)
self.fields['statement'].label = instance.get_label()
class Meta:
model = Answer
exclude=('question','person','course','teacher','questionaire')
As a side note, you can pass model's attributes values to the model's constructor:
answer = Answer(
question=Question.objects.get(id=request.session['question']),
person=student,
course=course,
questionnaire=questionnaire,
# User.objects.get(id=request.user.pk) will return request.user
# so it's just useless - just use request.user
tathya_user=request.user
)