1

I am trying to make a copy of this model

class Quiz(models.Model):

title = models.CharField(
    verbose_name=_("Title"),
    max_length=60, blank=False)

description = models.TextField(
    verbose_name=_("Description"),
    blank=True, help_text=_("a description of the quiz"))

url = models.SlugField(
    max_length=60, blank=False,
    help_text=_("a user friendly url"),
    verbose_name=_("user friendly url"))

category = models.ForeignKey(
    Category, null=True, blank=True,
    verbose_name=_("Category"))

random_order = models.BooleanField(
    blank=False, default=False,
    verbose_name=_("Random Order"),
    help_text=_("Display the questions in "
                "a random order or as they "
                "are set?"))

max_questions = models.PositiveIntegerField(
    blank=True, null=True, verbose_name=_("Max Questions"),
    help_text=_("Number of questions to be answered on each attempt."))

answers_at_end = models.BooleanField(
    blank=False, default=False,
    help_text=_("Correct answer is NOT shown after question."
                " Answers displayed at the end."),
    verbose_name=_("Answers at end"))

exam_paper = models.BooleanField(
    blank=False, default=False,
    help_text=_("If yes, the result of each"
                " attempt by a user will be"
                " stored. Necessary for marking."),
    verbose_name=_("Exam Paper"))

single_attempt = models.BooleanField(
    blank=False, default=False,
    help_text=_("If yes, only one attempt by"
                " a user will be permitted."
                " Non users cannot sit this exam."),
    verbose_name=_("Single Attempt"))

pass_mark = models.SmallIntegerField(
    blank=True, default=0,
    verbose_name=_("Pass Mark"),
    help_text=_("Percentage required to pass exam."),
    validators=[MaxValueValidator(100)])

success_text = models.TextField(
    blank=True, help_text=_("Displayed if user passes."),
    verbose_name=_("Success Text"))

fail_text = models.TextField(
    verbose_name=_("Fail Text"),
    blank=True, help_text=_("Displayed if user fails."))

draft = models.BooleanField(
    blank=True, default=False,
    verbose_name=_("Draft"),
    help_text=_("If yes, the quiz is not displayed"
                " in the quiz list and can only be"
                " taken by users who can edit"
                " quizzes."))

Basically this is a quiz. It has its own questions which can be added in the django admin. Now when I make a copy of this model the questions for the copy aren't there.

I make a copy by using this.

Obj=Quiz.objects.get(pk=pkofquiziwanttocopy)
Obj.pk=None
Obj.save()

This makes a copy of the quiz but the questions arent there. Now there is some code in the admin.py regarding the qurstions for the quiz. Which might be helpful.

class QuizAdminForm(forms.ModelForm):
"""
below is from
https://stackoverflow.com/questions/11657682/
django-admin-interface-using-horizontal-filter-with-
inline-manytomany-field
"""

class Meta:
    model = Quiz
    exclude = []

questions = forms.ModelMultipleChoiceField(
    queryset=Question.objects.all().select_subclasses(),
    required=False,
    label=_("Questions"),
    widget=FilteredSelectMultiple(
        verbose_name=_("Questions"),
        is_stacked=False))

def __init__(self, *args, **kwargs):
    super(QuizAdminForm, self).__init__(*args, **kwargs)
    if self.instance.pk:
        self.fields['questions'].initial =\
            self.instance.question_set.all().select_subclasses()

def save(self, commit=True):
    quiz = super(QuizAdminForm, self).save(commit=False)
    quiz.save()
    quiz.question_set = self.cleaned_data['questions']
    self.save_m2m()
    return quiz

How can I make a copy of this model with the questions of the original ?

Cœur
  • 37,241
  • 25
  • 195
  • 267
  • I see no questions in the original, but you have a ton of code (that doesn't seem related to your question), and your indentation is horribly wrong... Maybe more people will answer if you fix those two issues..? – thebjorn Jan 08 '18 at 00:07

2 Answers2

4

Assuming, the Question model has a foreign key to Quiz, you can clone the questions just like you cloned the quiz:

quiz = Quiz.objects.get(pk=pkofquiziwanttocopy)
quiz.pk = None
quiz.save()
old_quiz = Quiz.objects.get(pk=pkofquiziwanttocopy)
for quest in old_quiz.question_set.all():
    quest.pk = None
    quest.quiz = quiz
    quest.save()
user2390182
  • 72,016
  • 6
  • 67
  • 89
  • It didn't end up being the answer but it helped me to find it. I took your answer and changed it up a bit and now it works. I will post the code down bellow but thank you very much for leading me in the right direction. – Enes Beganovic Jan 08 '18 at 11:43
  • if you set questions = quiz.question_set.all(), before the save would it reference the old quiz's questions – merhoo Feb 28 '23 at 18:16
  • 1
    @merhoo The assignment itself would not yet evaluate the queryset. So it would only reference the old questions if you evaluated it before the saves, e.g. `questions = list(quiz.question_set.all())` – user2390182 Feb 28 '23 at 20:05
  • how would you ensure that this happens atomically? – merhoo Feb 28 '23 at 20:38
  • Wrap whatever you want atomic in `with transaction.atomic()`. But the line of code from my previous comment is a read access anyway and does nt care too much about atomicity. – user2390182 Mar 01 '23 at 10:41
0

What ended up being the solution is:

quiz = Quiz.objects.get(pk=pkofquiziwanttocopy)
quiz.pk = None
quiz.save()
old_quiz = Quiz.objects.get(pk=pkofquiziwanttocopy)

quiz.question_set=old_quiz.question_set.all()