4

I have a model named Entry that has the following fields

from django.contrib.auth.models import User

class Entry(models.Model):
    start = models.DateTimeField()
    end = models.DateTimeField()
    creator = models.ForeignKey(User)
    canceled = models.BooleanField(default=False)

When I create a new entry I don't want to be created if the creator has allready a created event between the same start and end dates. So my Idea was when user posts data from the creation form

if request.method == 'POST':
    entryform = EntryAddForm(request.POST)
    if entryform.is_valid():
        entry = entryform.save(commit=False)
        entry.creator = request.user

        #check if an entry exists between start and end
        if Entry.objects.get(creator=entry.creator, start__gte=entry.start, end__lte=entry.end, canceled=False):
            #Add to message framework that an entry allready exists there
            return redirect_to('create_form_page')
        else:
            #go on with creating the entry

I was thinking that maybe a unique field and checking properly db integrity would be better but it is the canceled field that's troubling me in to how to choose the unique fields. Do you think there will be somethng wrong with my method? I mean does it make sure that no entry will be set between start and end date for user is he has allready saved one? Do you think its better this code do go to the pre-save? The db will start empty, so after entering one entry, everything will take its course (assuming that...not really sure...)

orokusaki
  • 55,146
  • 59
  • 179
  • 257
Apostolos
  • 7,763
  • 17
  • 80
  • 150
  • Maybe so? `Entry.objects.filter(creator=entry.creator, start__gte=entry.start, end__lte=end, canceled=True).exists()` – crazyzubr Dec 02 '13 at 08:01
  • In the if statement right? – Apostolos Dec 02 '13 at 08:02
  • Sure. instead `Entry.objects.get...` To avoid `Entry.DoesNotExist` exception – crazyzubr Dec 02 '13 at 08:07
  • `end` needs to be declared somewhere, possibly as `entry.end`. Variable names are all over the place, really - we have `entryform` in one line, `form` in another. And I have deep doubts about that query that checks for existing events - do you really want to check only for canceled events? I think the start and end time filter is wrong too, but I'm feeling a little too lazy to work out what it should be right now - if a new event runs from noon to 1, existing events from both 11:30-12:30 and from 12:30 - 1:30 are conflicts. – Peter DeGlopper Dec 02 '13 at 08:20
  • Typo in `entry = entryform.save(commit=False)` which I assume is only in the question. – Henrik Andersson Dec 02 '13 at 08:21
  • 3
    I think the correct query for overlapping events, based just on the time fields, is `Entry.objects.filter(start_time__lt=entry.end_time, end_time__gt=entry.start_time)` That is, conflicting events must start before the proposed new event ends and must end after the proposed new events starts. Obviously you might need to filter by cancel status as well. – Peter DeGlopper Dec 02 '13 at 08:28
  • Yes it's a typo sorry just saw it. I see your point @PeterDeGlopper...I will look in to it a bit more thoroughly...thank you. Will edit and correct typos too – Apostolos Dec 02 '13 at 08:35
  • 2
    I'd put these range checks in `EntryAddForm.clean()` rather than in the view. – Kevin Stone Dec 02 '13 at 08:41
  • I ment canceled=False. I want to check for non-canceled entries – Apostolos Dec 02 '13 at 09:05

1 Answers1

0

You need to use Q for complex queries.

from django.db.models import Q
_exists =   Entry.objects.filter(Q(
                     Q(Q(start__gte=entry.start) & Q(start__lte=entry.end)) | 
                     Q(Q(end__gte=entry.start) & Q(end__lte=entry.end)) | 
                     Q(Q(start__lte=enrty.start) & Q(end__gte=entry.end))
))
if _exists:
    "There is existing event"
else:
    "You can create the event"

Since I do not test this, I use Q objects whereever I thought would be necessary.

Using this query, you will not need any unique check.

Mp0int
  • 18,172
  • 15
  • 83
  • 114