-1

I have banner model

class SiteBanner( models.Model ):
   banner = models.FileField( storage=s3_storage, upload_to = 'banners/', null=True, blank=True )
   order = models.PositiveIntegerField ( default=0 ) 
   default = models.BooleanField ( default = False )

I want to show different banner on every user click or on every site page. It will be disabled when i set default to True for a banner then this banner will show on every page. Is this possible? if yes then what will be an elegant solution? Is cookies be helpful to achieve this?

Ahsan
  • 11,516
  • 12
  • 52
  • 79

2 Answers2

2

First you need a context processor to get the banner for you:

# your_app/context_processors.py

def get_banner(request):
    seen_banners = request.session.get('seen_banners', [])
    try:
        banner = SiteBanner.objects.exclude(pk__in=seen_banners, default=False).order_by('order')[0]
    except IndexError:
        if seen_banners:
            del request.session['seen_banners']
            try:
                banner = SiteBanner.objects.exclude(default=False).order_by('order')[0]
            except IndexError:
                return {}
        else:
            return {}
    else:
        if seen_banners:
            request.session['seen_banners'].append(banner.pk)
            request.session.modified = True
        else:
            request.session['seen_banners'] = [banner.pk]
        return {'banner': banner}

Tie that into your settings.py TEMPLATE_CONTEXT_PROCESSORS setting. Then, in each of your templates, you'll automatically have a banner variable you can use to access the banner to load (assuming there's at least one banner available).

The context processor is a little complicated, so I should probably walk you through it. First, it attempts to retrieve a list of "seen" banners from the session. This is then used to exclude those banners from the queryset (so the so the same user won't get the same banner again). An IndexError will occur if there's not at least one available banner, so we catch that. If it's because the user has already seen them all, we kill the session var and try to get a banner again without excluding any. Otherwise, there's really no banners available, so we simply return an empty context.

If we are able to get a banner, then we add its id to the session var so it won't be repeated and then we return the context with the banner included.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • please do some changes it doesn't work at very first, for example i have 5 banners with default `False`, so at user first click there is no `seen_banners` its `[]` and query `banner = SiteBanner.objects.exclude(pk__in=[], default=False).order_by('order')[0]` doesn't get any element `IndexError` will be thrown, there it returns `{}` every time. please change it to `banner = SiteBanner.objects.exclude(Q(pk__in=seen_banners) | Q(default=True)).order_by('order')[0]` and also when all banners shown then what banner will be return?? – Ahsan Jan 18 '12 at 12:33
  • 7
    No offense, but I'm not here to write your code for you. I *generously* gave you a pretty extended example to work from base upon my best understanding of what you wanted. If it's not quite right, then modify it to work in your circumstances. You have enough to work from there. – Chris Pratt Jan 18 '12 at 14:53
1

(1) Have every page inherit from a template that displays the banner. Find out about template inheritance here: https://docs.djangoproject.com/en/dev/topics/templates/#template-inheritance

(2) Use a context processor to select the banner, and insert it into the context; that is to say, make it available as a variable inside your templates. Find out about context processors here: https://docs.djangoproject.com/en/dev/ref/templates/api/#subclassing-context-requestcontext

Writing the code is left as an exercise to the reader.

Marcin
  • 48,559
  • 18
  • 128
  • 201
  • i didn't understand your solution, can you please explain it? by code it will be more easy to understand – Ahsan Jan 17 '12 at 12:27
  • what i understood from your solution is, assign each banner to every template of site and send it to template through `requestcontext` is it? or something else you are meaning? – Ahsan Jan 17 '12 at 13:04
  • @Ahsan: I wouldn't quite put it like that. Every template should have the code to display your banner, if the banner is present in the context; and the context processor selects the banner, and puts it in the requestcontext. – Marcin Jan 17 '12 at 13:09
  • @Marchin: One more thing please, is user number of clicks be available in context processor, or something on which, i show the different banner each time? – Ahsan Jan 17 '12 at 13:57
  • @Ahsan: You write the context processor. It will do whatever you like. – Marcin Jan 17 '12 at 14:02
  • I wrote context processor but don't know how to select different banner for each user click? please help! – Ahsan Jan 17 '12 at 14:06
  • @Ahsan: If you have another problem, then ask a new question. You might also like to accept this answer. – Marcin Jan 17 '12 at 14:07
  • my question title is same as my previous comment :( please if you know tell me in edits. – Ahsan Jan 17 '12 at 14:23
  • @Ahsan: That is not how things are done here. You have a new question, you should ask it as a new question. – Marcin Jan 17 '12 at 14:25
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/6811/discussion-between-ahsan-and-marcin) – Ahsan Jan 18 '12 at 09:24
  • 1
    @Ahsan: Let us not. If you have a new question, post a new question. – Marcin Jan 18 '12 at 09:34