Last active
October 24, 2017 08:00
-
-
Save LowerDeez/bcb4e0c804a6118b6c38aa7099b68042 to your computer and use it in GitHub Desktop.
Django. Simple Follower System
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Include before main script file and after jquery (or after jquery if everything in one file) | |
$(document).ready(function(){ | |
... | |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Cross Site Request Forgery protection~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
function getCookie(name) { | |
var cookieValue = null; | |
if (document.cookie && document.cookie != '') { | |
var cookies = document.cookie.split(';'); | |
for (var i = 0; i < cookies.length; i++) { | |
var cookie = jQuery.trim(cookies[i]); | |
// Does this cookie string begin with the name we want? | |
if (cookie.substring(0, name.length + 1) == (name + '=')) { | |
cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); | |
break; | |
} | |
} | |
} | |
return cookieValue; | |
} | |
var csrftoken = getCookie('csrftoken'); | |
function csrfSafeMethod(method) { | |
// these HTTP methods do not require CSRF protection | |
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); | |
} | |
$.ajaxSetup({ | |
beforeSend: function(xhr, settings) { | |
if (!csrfSafeMethod(settings.type) && !this.crossDomain) { | |
xhr.setRequestHeader("X-CSRFToken", csrftoken); | |
} | |
} | |
}); | |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Cross Site Request Forgery protection~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Contact(models.Model): | |
user_from = models.ForeignKey(User, related_name='rel_from_set') | |
user_to = models.ForeignKey(User, related_name='rel_to_set') | |
created = models.DateTimeField(auto_now_add=True, db_index=True) | |
class Meta: | |
ordering = ('-created',) | |
unique_together = ('user_from', 'user_to') | |
def __str__(self): | |
return '{} follows {}'.format(self.user_from, self.user_to) | |
# Add following field to User dynamically | |
User.add_to_class('following', | |
models.ManyToManyField('self', | |
through=Contact, | |
related_name='followers', | |
symmetrical=False)) | |
""" | |
The relationship includes symmetrical=False. When you defie a ManyToManyField to the model itself, | |
Django forces the relationship to be symmetrical. In this case, we are setting symmetrical=False to | |
define a non-symmetric relation. This is, if I follow you, it doesn't mean you automatically follow me. | |
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- Somewhere in profile page --> | |
{% with total_followers=user.followers.count %} | |
<span class="count"> | |
<span class="badge total" style="margin: 4px;">{{ total_followers }}</span> | |
follower{{ total_followers|pluralize }} | |
</span> | |
{% if request.user != user %} | |
<div style="margin-top: 5px;"> | |
<a href="#" | |
data-id="{{ user.id }}" | |
data-action="{% if request.user in user.followers.all %}un{% endif %}follow" | |
data-url="{% url "accounts:user_follow" %}" | |
class="btn btn-success follow button"> | |
{% if request.user not in user.followers.all %} | |
Follow | |
{% else %} | |
Unfollow | |
{% endif %} | |
</a> | |
</div> | |
{% endif %} | |
{% endwith %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(document).ready(function() { | |
$('a.follow').click(function(e){ | |
e.preventDefault(); | |
$.ajax({ | |
type: 'POST', | |
url: $(this).data('url'), | |
data: { | |
id: $(this).data('id'), | |
action: $(this).data('action') | |
}, | |
dataType: "json", | |
success: function (data, textStatus) { | |
if (data['status'] == 'ok') | |
{ | |
var previous_action = $('a.follow').data('action'); | |
// toggle data-action | |
$('a.follow').data('action', previous_action == 'follow' ? 'unfollow' : 'follow'); | |
// toggle link text | |
$('a.follow').text(previous_action == 'follow' ? 'Unfollow' : 'Follow'); | |
// update total followers | |
var previous_followers = parseInt($('span.count .total').text()); | |
$('span.count .total').text(previous_action == 'follow' ? previous_followers + 1 : previous_followers - 1); | |
} | |
}, | |
error: function(xhr, status, e) { | |
alert(status, e); | |
} | |
}); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
url(r'^users/follow/$', views.UserFollowView.as_view(), name='user_follow'), |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
You can integrate it in your account app. | |
Or you can make it as app: | |
1) create new app for followers | |
2) add it to installed apps. | |
3) make migrations and migrate | |
3) include url to main file |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class UserFollowView(LoginRequiredMixin, View): | |
def post(self, request, *args, **kwargs): | |
if request.is_ajax(): | |
user_id = request.POST.get('id') | |
action = request.POST.get('action') | |
if user_id and action: | |
try: | |
user = User.objects.get(id=user_id) | |
except User.DoesNotExist: | |
return JsonResponse({'status': 'ko'}) | |
if action == 'follow': | |
Contact.objects.get_or_create(user_from=request.user, | |
user_to=user) | |
else: | |
Contact.objects.filter(user_from=request.user, | |
user_to=user).delete() | |
return JsonResponse({'status': 'ok'}) | |
else: | |
return JsonResponse({'status': 'ko'}) | |
else: | |
return HttpResponseBadRequest() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment