7

Say I have an extremely simple model that is just a list of words:

class WordList(models.Model):
    word = models.CharField(max_length=60)

After a user submits a form, I want to...

  • Retrieve four random words
  • Combine them into a single string
  • Ensure a duplicate string has not been previously generated and if so, run it again
  • Save that to the database when it's good
  • Return the result to the user.

I know how to get four random words:

WordList.objects.order_by('?')[:4]

I know how to make this a context and return it to a template, at which point I can do whatever with it, but I'm stumped on how I do this behind the scenes so I can do the rest of my stuff before returning it to the user. The final string should look like this:

these-are-my-words

Furthermore, where in my app do I do this? I come from PHP and there, I would have a functions.php file or something to perform backend stuff and keep it out of the presentation. I've found a few other posts from people stating they use a functions.py, but I'm not sure how to include external pages that aren't in the same folder as the existing views.py. If I do:

from functions import myfunc

It only works if functions.py is in the folder as wherever I am importing it from.

2
  • So what is your question? You are asking about querysets or how to do imports in Python? Commented Feb 24, 2013 at 18:28
  • @miki725 Yes. What's the best way to do this and where? I found an answer for the first part and at the moment have it in my View. Is there somewhere else this should live? I can figure out how to do imports but what is Django best practice? Commented Feb 25, 2013 at 4:54

4 Answers 4

18

To turn your queryset into a string, use python's join function.

your_string = '-'.join([str(i) for i in WordList.objects.order_by('?')[:4]])

I suspect that this code should actually live in one of your views and never touch your database, but that's hard to say without knowing what your app is doing. (Surely you're passing this string to a template and rendering it onto an html page?)

Sign up to request clarification or add additional context in comments.

4 Comments

That was throwing an error that it expected a string but was being given a list. Join did seem like the right thing to do but it was just not working. I'm posting how I ended up doing as the answer, but let me know if there was a better way to do it.
Join is much better method compared to your solution. I make an edit which should solve your issue.
Excellent, thanks guys. That is exactly what I was looking for. It gives a good example of how I can interact with QuerySets before they hit templates. In Python terms, since a QuerySet is not a list, what is it?
I'm not sure how to answer that. It's a complicated object that represents a database query (amongst other things). For more info you could have a look at the source code or the docs docs.djangoproject.com/en/1.4/ref/models/querysets)
0

Here's what ended up working. The trick was that I didn't realize the QuerySet could just be accessed the way one would a Python list.

dbQuery = WordList.objects.order_by('?')[:4]
result = dbQuery[0]
for word in dbQuery[1:]:
    result = "%s-%s" % (result, word)

I feel like there must be a better way to do that. Join, as suggested, did not work, I kept getting an error that it expected a string even though all the docs say that it's used to join lists, not strings, so I'm not sure where the breakdown was.

1 Comment

sorry, I should have checked my code. miki725 has corrected my answer, replacing the queryset with an iterable list of strings.
0

While your posted solution "works", it's a very PHP way of doing things.

A more Django way:

In your application's models.py file:

from django.db import models


class Word(models.Model):
    word = models.CharField(max_length=60, blank=False, null=False, unique=True)

    def __unicode__(self):
        return u'%s' % self.word


class RandomWordString(models.Model):
    string = models.CharField(max_length=255, blank=False, null=False, unique=True)

    def __unicode__(self):
        return u'%s' % self.string

    @staticmethod
    def generate(length):
        words = Word.objects.order_by('?')[:(length + 1)]
        possible_word_string = '-'.join(words.values_list('word', flat=True))
        try:
            RandomWordString.objects.get(string=possible_word_string)  # See if we've already generated this sequence
            return RandomWordString.generate(length)  # Call ourselves again if we have
        except RandomWordString.DoesNotExist:
            return possible_word_string  # If we haven't, return the value

    def save(self, *args, **kwargs):
        if not self.string or len(self.string) < 1:
            self.string = RandomWordString.generate(3)
        super(RandomWordString, self).save(*args, **kwargs)

Then, from any view, or anywhere else:

from words.models import RandomWordString
seq = RandomWordString.generate(3)

Because we overrode save, we can also just do:

from words.models import RandomWordString
string = RandomWordString.objects.create()
string.save()

This places all the logic within the models themselves, which is slightly better than having it in a view (though it's completely a matter of taste).

Beyond what I've posted, you'd also want to add some logic to RandomWordString.generate to make sure you don't loop indefinitely.

4 Comments

In my opinion you are right that it's better to have logic within models however you have you should not overuse it. One of the principles of databases is not to store duplicate data which you can easily generate unless you have a very good reason. In this case it is trivial to generate the wanted string hence the logic better fits within a view instead of models. Additionally storing the generated string in up brings a whole bunch of issues like what happens when a Word is updates. Do all string which use it should get updated?
I'm not storing links between the WordList and the Word, as that would be crazy (though I guess you could do it).
In this case, it is going into the database immediately. There's a separate table that keeps track of the generated strings and some other information that is supplied by the user. There's no concern with it duplicating data or dealing with changing words. Very simple little project, rebuilding something I did in PHP to learn a bit more of Django. I get the feeling that this should go in the Model since it mainly interacts with the database.
Right. If it doesn't, not a huge deal, but it's nice to keep data-related stuff on the model as a force of habit.
0

You have to represent strings for combining queryset into single string with Django by join function. So, you can use for loop to extract strings from your objects of the class:

youroutputishere  = '-'.join([str(counterofobjects) for counterofobjects in yourobjects])

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.