Django: Please help with this queryset - django

I have a Django model, shown below, that I use to keep track of which ip addresses visit my site and when.
class Visit(models.Model):
created = models.DateTimeField(default=datetime.utcnow)
ip = models.IPAddressField(editable=False)
I'd like to write a method on this model that returns the number of days in took for the last 100 visits from a particular IP. Multiple visits in a single day (hour etc) from an IP all count as separate visits. So, if someone visted the site 100 times in the past 2 days, it would return 2, 100 times in the past 8 days, it would return 8, and so on.

You probably want to change the default= for created_on to be auto_now_add since the datetime.utcnow doesn't update if you're using servers other than the dev server:
class Visit(models.Model):
created = models.DateTimeField(auto_now_add=True,editable=False)
ip = models.IPAddressField(editable=False)
from datetime import datetime
def days_for_100(ip_addr):
now = datetime.now()
dates = Visit.objects.filter(ip=ip_addr)
if dates.count()<100:
latest = dates.latest('-created')
else:
latest = dates.dates('created','day',order='DESC')[99]
return (now-latest).days # timedelta.days
This returns how many days earlier the 100th visit ago happened (or how long ago the first visit occurred if there are less than 100 visits)

An easy approach is to get the last 100 Visit objects for an ip address and count the number of unique created objets in them.
def num_of_dates(ip_addr)
dates = [v.created for v in Visit.objects.filter(ip=ip_addr).order_by('-created')[0:100]]
unique_dates = set(dates)
return len(unique_dates)

Related

How to calculate unique IP's in Django (1 unique IP per day)?

**
I'would like to calculate on many unique IP's have visited a page from my website, with maximum visit per IP per day = 1.
**
I have a model with various fields.
Two of them are :
- date: created_at.strftime("%d-%m-%y, %H:%M")
- ip: request_ip
A example of these two informations would be :
- 2019-07-15 17:43:09
- 127.0.0.1
I tried a combinaisons of various Django's methods like : values, distinct, filter, etc. But I can't get the result I want.
Because I cannot change the format of my fields, the distinct method won't work because the date changes every second. It would have been nice to have a date format like ("%d-%m-%y) = without the seconds, minutes and hours.
This is why the following example won't work. I have as a result, all the IPs in my database.
Example :
ip_number = reward_views.objects.values('request_ip', 'created_at').distinct().count()
So I tried to put the informations, IPs and dates, in lists in order to modify the date format and do better manipulations on it.
try:
all_views = reward_views.objects.all().order_by('-updated_at')
except:
return HttpResponse(status=500)
all_dates = []
for view in all_views:
all_dates.append({
'reward_creation_date': view.created_at.strftime("%d-%m-%y"),
'user_ip': view.request_ip,
})
print (all_dates)
But I can't find any right method to calculate the total of unique IP per day, and ultimately, the total of unique IP of all time (with a maximum visit per IP per day = 1).
Should I calculate this without any method ? Should I put all IPs in one list and all dates in another one (all of them ordered the same way), and then try to delete all the same occurrences ? But how can apply the 24H rule ?
Thanks for your suggestions !
Found it !
I actually discovered the possibility to put raw SQL in Django.
So I added this :
cursor.execute('''
SELECT COUNT(
DISTINCT tracker.request_ip,
DATE(tracker.updated_at)
)
FROM tracker;
''')
unique_ips = cursor.fetchone()
Don't forget to import :
from django.db import connection
It worked just fine ! Here's the link to the documentation : https://docs.djangoproject.com/en/dev/topics/db/sql/#passing-parameters-into-raw
Still, if someone knows how to do it without any raw SQL, let me know ! :)

Django - creating and saving multiple object in a loop, with ForeignKeys

I am having trouble creating and saving objects in Django. I am very new to Django so I'm sure I'm missing something very obvious!
I am building a price comparison app, and I have a Search model:
Search - all searches carried out, recording best price, worst price, product searched for, time of search etc. I have successfully managed to save these searches to a DB and am happy with this model.
The two new models I am working with are:
Result - this is intended to record all search results returned, for each search carried out. I.e. Seller 1 £100, Seller 2 £200, Seller 3, £300. (One search has many search results).
'Agent' - a simple table of Agents that I compare prices at. (One Agent can have many search Results).
class Agent(models.Model):
agent_id = models.AutoField(primary_key=True)
agent_name = models.CharField(max_length=30)
class Result(models.Model):
search_id = models.ForeignKey(Search, on_delete=models.CASCADE) # Foreign Key of Search table
agent_id = models.ForeignKey(Agent, on_delete=models.CASCADE) # Foreign Key of Agent table
price = models.FloatField()
search_position = models.IntegerField().
My code that is creating and saving the objects is here:
def update_search_table(listed, product):
if len(listed) > 0:
search = Search(product=product,
no_of_agents=len(listed),
valid_search=1,
best_price=listed[0]['cost'],
worst_price=listed[-1]['cost'])
search.save()
for i in range(len(listed)):
agent = Agent.objects.get(agent_name = listed[i]['company'])
# print(agent.agent_id) # Prints expected value
# print(search.search_id) # Prints expected value
# print(listed[i]['cost']) # Prints expected value
# print(i + 1) # Prints expected value
result = Result(search_id = search,
agent_id = agent,
price = listed[i]['cost'],
position = i + 1)
search.result_set.add(result)
agent.result_set.add(result)
result.save()
Up to search.save() is working as expected.
The first line of the for loop is also correctly retrieving the relevant Agent.
The rest of it is going wrong (i.e. not saving any Result objects to the Result table). What I want to achieve is, if there are 10 different agent results returned, create 10 Result objects and save each one. Link each of those 10 objects to the Search that triggered the results, and link each of those 10 objects to the relevant Agent.
Have tried quite a few iterations but not sure where I'm going wrong.
Thanks

How to sum two columns in query on Django

I have the following model on my postgresql database:
class UrlXML(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True)
url = models.TextField()
is_active = models.BooleanField(default=True, db_index=True)
run_last_time = models.DateTimeField(blank=True, null=True)
run_frequency = models.IntegerField(default=24)
Every hour I need to get from database url that need to be downloaded based when on my current time and if the last time it ran was higher then the frequency.
I manage to create the raw query, but I can't manage to create it in Django Queryset.
Here is the following query:
select (run_last_time + INTERVAL '1 hours' * run_frequency), run_frequency, NOW(), run_last_time from urlxml where is_active=True and (run_last_time + INTERVAL '1 hours' * run_frequency) <= NOW();
Example:
Current time is 2017-04-03 11:00:00
I have two url in database:
Url A: Ran last time 2017-04-03 08:00:00 and its frequency is 6 hours
Url B: Ran last time 2017-04-02 11:00:00 and its frequency is 24 hours
When I execute the function at 2017-04-03 11:00:00 (within the margin of + and - 30 minutes), it must bring me the Url B only, 'cause the last time it ran was 24 hours ago.
I managed to find a solution using the extra in the Queryset.
Here it is:
UrlXML.objects.filter(is_active=True).extra(
where={"run_last_time + INTERVAL '1 hours' * run_frequency <= NOW()"}
)
I don't know if this is the best way to do this, but is the only one I manage to find.
If there are better ways to do it, I'm open to suggestions.
If you were to change your model slightly, you could use Query Expressions.
# models.py
class UrlXML(models.Model):
...
run_frequency = models.DurationField(default=timedelta(hours=24))
UrlXML.objects \
.annotate(expires=ExpressionWrapper(
F('run_last_time') + F('run_frequency'),
output_field=DateTimeField())) \
.filter(expires__lte=datetime.now())
This solutions is also a bit more robust, as you can use datetime.timedelta instead of hours only.

django 'F-expression' query

I have two models, subject and visit. I'm trying to select visits where the age in months was over a certain amount, but cannot simply filter on age_months, as it is not a field, but rather a calculated value associated with the visit model.
We are getting age_months from a form and then trying to convert it to a date (subject birthdate plus the age in months converted to a timedelta) then use F-filtering in our view to select visits where the visit_date is greater than this calculated subject age
Here is the code:
vform = VisSearchForm(request.POST,prefix='v')
if vform.is_valid():
e = vform.cleaned_data
vresults = Visit.objects.all()
if e['age_months']:
vresults.filter(visit_date__gt=F('subject__birthdate') + timedelta(days=30.44*e['age_months']))
the birthdate field is connected to the subject model, not the visit model and visit_date is connected to the visit model.
When I print the timedelta part I get a value, but when I print the F('subject__birthdate') + timedelta(days=30.44*e['age_months'])
I get
(+: (DEFAULT: ), 700 days, 2:52:48)
When what I want is the F('subject__birthdate') to return a date and not (+: (DEFAULT: )
It won't. The F object is a reference to a database field and holds absolutely no meaning outside the context of a database query.
Even when the query is built, it is still a reference to another database field, and doesn't hold an actual value.

Django select an object based on a date

This is driving me crazy. I've used all the lookup_types and none seem to work.
I need to select an object that was created two weeks ago from today.
Here's what I've got:
twoweeksago = datetime.datetime.now() - datetime.timedelta(days=14)
pastblast = Model.objects.filter(user=user, created=twoweeksago, done=False)
The model has a created field that does this: created = models.DateTimeField(auto_now_add=True, editable=False)
But my query isn't returning everything. Before you ask, yes, there are records in the db with the right date.
Can someone make a suggestion as to what I'm doing wrong?
Thanks
DateTimeField is very different from DateField, if you do
twoweeksago = datetime.datetime.now() - datetime.timedelta(days=14)
That is going to return today's date, hour, minute, second minus 14 days, and the result is going to include also hours minutes seconds etc. So the query:
pastblast = Model.objects.filter(user=user, created=twoweeksago, done=False)
Is going to find for a instance was created just in that exact time, If you only want to care about the day, and not hours, minutes and seconds you can do something like
pastblast = Model.objects.filter(user=user, created__year=twoweeksago.year, created__month=twoweeksago.month, created__day=twoweeksago.day, done=False)
Check the django docs:
https://docs.djangoproject.com/en/1.4/ref/models/querysets/#year