Reputation: 10812
In GitHub, you will notice that when you want to reference existing issues or mark an issue under projects GitHub intelligently will show you the most recent items that you visited recently first.
Example of showing recent projects in order of last visited
Example of showing recent issues in order of last visited
I’m using django to build a web app and i like to incorporate the same feature.
I have two ideas but none implemented yet:
1. One giant table storing all the visits by individual users
Should I have one giant table that stores all the items that all users visit? If so how do I then join that back with the main table I am supposed to search on to produce the sort correctly?
2. Add a visited
column to the individual main tables
I cannot obviously add a new date time column called “visited” to the main table as that will mean there is no personalization.
What am I missing in terms of understanding?
Also, is it a fair assumption that I need to limit the number of most recent items stored per user?
Meaning to say, using the GitHub example, only store a maximum of 5 most recent projects or issues per user.
Upvotes: 3
Views: 725
Reputation: 5241
I think using Visit as a separate model is a good choice to keep separation.
It's fine to just limit the return result such as User.recent_visits.all()[:5] in the DRF api.
The solution below ensures User.recent_visits.all() will return 5 last recent visits without slicing, and avoid a huge list.
The "pattern" I've used successfully in the past which would roughly translate to this context as:
class VisitQuerySet(models.QuerySet):
MAX_LRV = 5 # Max Last Recent Visits
def add_item(self, user, item):
with transaction.atomic():
# to avoid duplicate items
lrv, created = self.get_or_create(user=user, item=item)
if not created:
lrv.save() # update timestamp
return lrv
self.prune_lrv(user)
return lrv
def prune_lrv(self, user):
# One example of how to clean up the recent visits
qs = self.filter(user=user)
if qs.count() > self.MAX_LRV:
pending_delete = qs[self.MAX_LRV:]
self.filter(user=user, pk__in=pending_delete).delete()
class Visit(models.Model):
class Meta:
ordering = ('-last_visited',) # ordering important
user = ForeignKey(User, related_name="recent_visits")
item = ForeignKey(Item, related_name="visitors")
last_visited = DateTimeField(auto_now=True)
objects = VisitQuerySet.as_manager()
# Adding a item
Visit.objects.add_item(user, item)
#Get last 5 recent visits
request.user.recent_visits.all()
The list is maintained when adding new items, it's easy to change the MAX_LRV and should be straightforward to avoid duplicates (depending on how item is created in this scenario).
Upvotes: 0
Reputation: 1745
1. One giant table storing all the visits by individual users
Here's how I would go about implementing this:
class Visit(models.Model):
user = ForeignKey(User, related_name="recent_visits")
item = ForeignKey(Item, related_name="visitors")
last_visited = DateTimeField()
Then, in the User
serializer you could have a SerializerMethodField
to match a certain amount of recently visited items, like this:
class VisitSerializer(models.Model):
item = YourItemSerializer()
class Meta:
model = Visit
fields = ['item', 'last_visited']
class UserSerializer(serializers.ModelSerializer):
recent_visits = serializers.SerializerMethodField()
def get_items(self, obj):
# you could also get the value of 5 from a constant or your settings or
# even the API itself
items = Item.recent_visits.filter(user=obj)[:5]
serializer = VisitSerializer(instance=items, many=True)
return serializer.data
This could, however, be an overkill if you only want to keep a small list of recently visited items and don't want to have any logic based on the user's extended visit history.
Keeping the item information in the user model
If you only want to show certain info about the item (like id, name etc), you could keep that information in a JSONField
within the user model and only visit the item when the user wants its details. You could save database queries this way but will have to wrangle some JSON when the user visits or revisits an item.
This approach would also depend on how often the fields of Item
that you want to save in the User
model are changed or if the item gets deleted altogether.
Upvotes: 0
Reputation: 373
Would having a table that contains a FK to user, the url and the datetime last visited not work? Then create an API (drf) to return the most recent 5 for the user:
visits.objects.filter(user=request.user).order_by('-datetime_visited')[:5]
It would grow big, perhaps you can limit to only keeping 5 per user and delete as you insert. In every get request for each page you could:
last = visits.objects.filter(user=request.user).order_by('datetime_visited').first()
last.delete()
visits.objects.create(user=request.user, url=page_url, datetime_visited=datetime.datetime.now())
Probably a good idea to put a custom clean in there too: Limit the number of records in a Model that can have a value per user
Upvotes: 1