├── .github
└── FUNDING.yml
├── .gitignore
├── Procfile
├── README.md
├── feed
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_auto_20200812_0907.py
│ ├── 0003_like.py
│ ├── 0004_auto_20200813_1417.py
│ ├── 0005_auto_20200814_0848.py
│ ├── 0006_auto_20200815_0840.py
│ ├── 0007_auto_20200815_1205.py
│ ├── 0008_auto_20200815_1214.py
│ ├── 0009_auto_20200816_1516.py
│ ├── 0010_auto_20200816_1538.py
│ └── __init__.py
├── models.py
├── templates
│ └── feed
│ │ ├── create_post.html
│ │ ├── home.html
│ │ ├── layout.html
│ │ ├── post_detail.html
│ │ ├── search_posts.html
│ │ └── user_posts.html
├── tests.py
├── urls.py
└── views.py
├── manage.py
├── photoshare
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
├── requirements.txt
└── users
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── migrations
├── 0001_initial.py
├── 0002_auto_20200811_1941.py
├── 0003_auto_20200811_1943.py
├── 0004_auto_20200812_0907.py
├── 0005_remove_profile_name.py
├── 0006_auto_20200815_0840.py
└── __init__.py
├── models.py
├── templates
└── users
│ ├── edit_profile.html
│ ├── friend_list.html
│ ├── login.html
│ ├── logout.html
│ ├── password_reset.html
│ ├── password_reset_complete.html
│ ├── password_reset_confirm.html
│ ├── password_reset_done.html
│ ├── profile.html
│ ├── register.html
│ ├── search_users.html
│ └── users_list.html
├── tests.py
└── views.py
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | ko_fi: shubham1710
2 |
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
98 | __pypackages__/
99 |
100 | # Celery stuff
101 | celerybeat-schedule
102 | celerybeat.pid
103 |
104 | # SageMath parsed files
105 | *.sage.py
106 |
107 | # Environments
108 | .env
109 | .venv
110 | env/
111 | venv/
112 | ENV/
113 | env.bak/
114 | venv.bak/
115 |
116 | # Spyder project settings
117 | .spyderproject
118 | .spyproject
119 |
120 | # Rope project settings
121 | .ropeproject
122 |
123 | # mkdocs documentation
124 | /site
125 |
126 | # mypy
127 | .mypy_cache/
128 | .dmypy.json
129 | dmypy.json
130 |
131 | # Pyre type checker
132 | .pyre/
133 |
134 | # pytype static type analyzer
135 | .pytype/
136 |
137 | # Cython debug symbols
138 | cython_debug/
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: gunicorn photoshare.wsgi
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ByteWalk
2 | A social media photo sharing website built on [Django Framework](https://djangoproject.com/).
3 |
4 | The features available to users :-
5 | 1) Post images. A short description and tags can be included if desired by user.
6 | 2) can check everyone's post and also like and comment on the photos.
7 | 3) Users can edit or delete their photos too.
8 | 4) Users can search for posts by tags and for other users by their username.
9 | 5) Without authentication, users can view the posts but cannot like or comment.
10 | 6) Registeration system is complete with password reset option also available to users.
11 | 7) Users can edit their profile including profile pic and a short bio about them.
12 | 8) Users can view profile of others users and can send them friend requests.
13 | 9) Users can send friend request, cancel requests, accept requests, reject requests or even unfriend their friends.
14 | 10) Users are given suggestions for new friends based on mutual friendships.
15 |
16 | Technologies used :-
17 | 1) Frontend: HTML5, CSS (Bootstrap4 + Custom CSS), AJAX
18 | 2) Backend: [Django](https://djangoproject.com/)
19 | 3) Storage: [Google Cloud Storage](https://cloud.google.com/)
20 | 4) Deployment: [Heroku](https://heroku.com/)
21 |
22 | To try out the website, please download the repository in your local system and provide all the local variables as per your requirements. Switch to your local storage or keep using Google Cloud but make sure to create an account on Google Cloud Storage and create buckets and put in the id wherever needed in the code (settings.py file).
23 | In this way, you would be able to run it in your local machine! Do try it out!
24 |
25 | This repository is open for contribution. If you have any improvement in mind, you should make the pull request with relevant details and I shall add it.
26 |
--------------------------------------------------------------------------------
/feed/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shubham1710/ByteWalk/9f79221304c04816521de793356a06c76809245e/feed/__init__.py
--------------------------------------------------------------------------------
/feed/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import Post, Comments, Like
3 |
4 | admin.site.register(Post)
5 | admin.site.register(Comments)
6 | admin.site.register(Like)
7 |
--------------------------------------------------------------------------------
/feed/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class FeedConfig(AppConfig):
5 | name = 'feed'
6 |
--------------------------------------------------------------------------------
/feed/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from .models import Comments, Post
3 |
4 | class NewPostForm(forms.ModelForm):
5 | class Meta:
6 | model = Post
7 | fields = ['description', 'pic', 'tags']
8 |
9 | class NewCommentForm(forms.ModelForm):
10 |
11 | class Meta:
12 | model = Comments
13 | fields = ['comment']
--------------------------------------------------------------------------------
/feed/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-11 15:10
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 | import django.utils.timezone
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15 | ]
16 |
17 | operations = [
18 | migrations.CreateModel(
19 | name='Post',
20 | fields=[
21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22 | ('description', models.CharField(blank=True, max_length=255)),
23 | ('pic', models.ImageField(upload_to='photos')),
24 | ('date_posted', models.DateTimeField(default=django.utils.timezone.now)),
25 | ('tags', models.CharField(max_length=100)),
26 | ('user_name', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
27 | ],
28 | ),
29 | migrations.CreateModel(
30 | name='Comments',
31 | fields=[
32 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
33 | ('comment', models.TextField()),
34 | ('comment_date', models.DateTimeField(default=django.utils.timezone.now)),
35 | ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='details', to='feed.Post')),
36 | ('username', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='details', to=settings.AUTH_USER_MODEL)),
37 | ],
38 | ),
39 | ]
40 |
--------------------------------------------------------------------------------
/feed/migrations/0002_auto_20200812_0907.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-12 03:37
2 |
3 | from django.db import migrations
4 | import stdimage.models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('feed', '0001_initial'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='post',
16 | name='pic',
17 | field=stdimage.models.JPEGField(upload_to='photos'),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/feed/migrations/0003_like.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-12 07:20
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ('feed', '0002_auto_20200812_0907'),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='Like',
18 | fields=[
19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20 | ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='likes', to='feed.Post')),
21 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='likes', to=settings.AUTH_USER_MODEL)),
22 | ],
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/feed/migrations/0004_auto_20200813_1417.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-13 08:47
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('feed', '0003_like'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='post',
15 | name='tags',
16 | field=models.CharField(blank=True, max_length=100),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/feed/migrations/0005_auto_20200814_0848.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-14 03:18
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('feed', '0004_auto_20200813_1417'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='comments',
15 | name='comment',
16 | field=models.CharField(max_length=255),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/feed/migrations/0006_auto_20200815_0840.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-15 03:10
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('feed', '0005_auto_20200814_0848'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='post',
15 | name='pic',
16 | field=models.ImageField(upload_to='photos'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/feed/migrations/0007_auto_20200815_1205.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-15 06:35
2 |
3 | from django.db import migrations
4 | import stdimage.models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('feed', '0006_auto_20200815_0840'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='post',
16 | name='pic',
17 | field=stdimage.models.JPEGField(upload_to='path/to/img'),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/feed/migrations/0008_auto_20200815_1214.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-15 06:44
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('feed', '0007_auto_20200815_1205'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='post',
15 | name='pic',
16 | field=models.ImageField(upload_to='path/to/img'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/feed/migrations/0009_auto_20200816_1516.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-16 09:46
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
11 | ('feed', '0008_auto_20200815_1214'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='post',
17 | name='likes',
18 | field=models.ManyToManyField(related_name='likes', to=settings.AUTH_USER_MODEL),
19 | ),
20 | migrations.DeleteModel(
21 | name='Like',
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/feed/migrations/0010_auto_20200816_1538.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-16 10:08
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ('feed', '0009_auto_20200816_1516'),
13 | ]
14 |
15 | operations = [
16 | migrations.RemoveField(
17 | model_name='post',
18 | name='likes',
19 | ),
20 | migrations.CreateModel(
21 | name='Like',
22 | fields=[
23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
24 | ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='likes', to='feed.Post')),
25 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='likes', to=settings.AUTH_USER_MODEL)),
26 | ],
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/feed/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shubham1710/ByteWalk/9f79221304c04816521de793356a06c76809245e/feed/migrations/__init__.py
--------------------------------------------------------------------------------
/feed/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 | from django.urls import reverse
4 | from django.utils import timezone
5 |
6 | # This model is for any post that a user posts on the website.
7 | class Post(models.Model):
8 | description = models.CharField(max_length=255, blank=True)
9 | pic = models.ImageField(upload_to='path/to/img')
10 | date_posted = models.DateTimeField(default=timezone.now)
11 | user_name = models.ForeignKey(User, on_delete=models.CASCADE)
12 | tags = models.CharField(max_length=100, blank=True)
13 |
14 | def __str__(self):
15 | return self.description
16 |
17 |
18 | def get_absolute_url(self):
19 | return reverse('post-detail', kwargs={'pk': self.pk})
20 |
21 | # Comment model links a comment with the post and the user.
22 | class Comments(models.Model):
23 | post = models.ForeignKey(Post, related_name='details', on_delete=models.CASCADE)
24 | username = models.ForeignKey(User, related_name='details', on_delete=models.CASCADE)
25 | comment = models.CharField(max_length=255)
26 | comment_date = models.DateTimeField(default=timezone.now)
27 |
28 | # It stores the like info. It has the user who created the like and the post on which like was made.
29 | class Like(models.Model):
30 | user = models.ForeignKey(User, related_name='likes', on_delete=models.CASCADE)
31 | post = models.ForeignKey(Post, related_name='likes', on_delete=models.CASCADE)
32 |
--------------------------------------------------------------------------------
/feed/templates/feed/create_post.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% load crispy_forms_tags %}
2 | {% block searchform %}
3 |
13 | {% endblock searchform %} {% block content %}
14 |
15 |
41 |
42 | {% endblock content %} {% block jsfiles %}{% endblock jsfiles %}
43 |
--------------------------------------------------------------------------------
/feed/templates/feed/home.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% block searchform %}
2 |
12 | {% endblock searchform %} {% block content %}
13 |
14 |
15 |
16 | {% for post in posts %}
17 |
18 |
19 |

27 |
{{ post.user_name }}
32 |
Posted on {{ post.date_posted }}
35 |
36 |
{{ post.description }}
37 |
38 |

41 | {% if post.tags %}
42 |
43 |
44 | Tags: {{ post.tags }}
45 |
46 | {% endif %}
47 |
71 |
72 | {% endfor %}
73 |
74 |
75 |
76 |
77 | {% if is_paginated %}
78 | {% if page_obj.has_previous %}
79 | First
80 | Previous
85 | {% endif %}
86 | {% for num in page_obj.paginator.page_range %}
87 | {% if page_obj.number == num %}
88 | {{ num }}
89 | {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
90 | {{ num }}
91 | {% endif %}
92 | {% endfor %}
93 |
94 | {% if page_obj.has_next %}
95 | Next
100 | Last
105 | {% endif %}
106 |
107 | {% endif %}
108 |
109 | {% endblock content %}
110 |
111 | {% block jsfiles %}
112 |
135 | {% endblock jsfiles %}
136 |
--------------------------------------------------------------------------------
/feed/templates/feed/layout.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
4 |
5 |
6 |
7 |
11 |
16 |
17 |
21 |
25 |
30 | {% block cssfiles %}{% endblock cssfiles %}
31 | ByteWalk
32 |
33 |
34 |
35 |
82 | {% block content %}{% endblock content %}
83 |
84 |
91 |
92 |
93 |
94 |
95 |
96 | {% block jsfiles %}{% endblock jsfiles %}
97 |
98 |
99 |
--------------------------------------------------------------------------------
/feed/templates/feed/post_detail.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% load crispy_forms_tags %}
2 | {% block searchform %}
3 |
13 | {% endblock searchform %} {% block content %}
14 |
15 |
16 |
17 |
18 |
19 |

27 |
{{ post.user_name }}
32 |
Posted on {{ post.date_posted }}
35 |
36 |
{{ post.description }}
37 |
38 |

41 | {% if post.tags %}
42 |
43 |
44 | Tags: {{ post.tags }}
45 |
46 | {% endif %}
47 |
81 |
82 |
83 |
84 |
85 |
Comments
86 |
109 |
110 |
111 | {% if post.details.all %}
112 |
113 | {% for detail in post.details.all %}
114 |
115 |
116 |
123 |
124 |
{{ detail.username }}
129 |
{{ detail.comment_date }}
130 |
{{ detail.comment }}
131 |
132 |
133 | {% endfor %}
134 |
135 | {% else %}
136 |
No comments to show!
137 | {% endif %}
138 |
139 |
140 |
141 | {% endblock content %} {% block jsfiles %}
142 |
165 |
166 | {% endblock jsfiles %}
167 |
--------------------------------------------------------------------------------
/feed/templates/feed/search_posts.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% block searchform %}
2 |
12 | {% endblock searchform %} {% block content %}
13 |
14 |
15 |
16 | {% if not posts %}
17 |
18 |
No posts match the tag provided!
19 | {% endif %} {% for post in posts %}
20 |
21 |
22 |

30 |
{{ post.user_name }}
35 |
Posted on {{ post.date_posted }}
38 |
39 |
{{ post.description }}
40 |
41 |

44 | {% if post.tags %}
45 |
46 |
47 | Tags: {{ post.tags }}
48 |
49 | {% endif %}
50 |
74 |
75 | {% endfor %}
76 |
77 |
78 |
79 | {% endblock content %} {% block jsfiles %}
80 |
103 | {% endblock jsfiles %}
104 |
--------------------------------------------------------------------------------
/feed/templates/feed/user_posts.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% block searchform %}
2 |
12 | {% endblock searchform %} {% block content %}
13 |
14 |
15 |
16 | {% for post in posts %}
17 |
18 |
19 |

27 |
{{ post.user_name }}
32 |
Posted on {{ post.date_posted }}
35 |
36 |
{{ post.description }}
37 |
38 |

41 | {% if post.tags %}
42 |
43 |
44 | Tags: {{ post.tags }}
45 |
46 | {% endif %}
47 |
71 |
72 | {% endfor %}
73 |
74 |
75 |
76 | {% if is_paginated %}
77 | {% if page_obj.has_previous %}
78 | First
79 | Previous
84 | {% endif %}
85 | {% for num in page_obj.paginator.page_range %}
86 | {% if page_obj.number == num %}
87 | {{ num }}
88 | {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
89 | {{ num }}
90 | {% endif %}
91 | {% endfor %}
92 |
93 | {% if page_obj.has_next %}
94 | Next
99 | Last
104 | {% endif %}
105 | {% endif %}
106 | {% endblock content %} {% block jsfiles %}
107 |
130 | {% endblock jsfiles %}
131 |
--------------------------------------------------------------------------------
/feed/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/feed/urls.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.urls import path, include
3 | from . import views
4 | from .views import PostUpdateView, PostListView, UserPostListView
5 |
6 | urlpatterns=[
7 | path('', PostListView.as_view(), name='home'),
8 | path('post/new/', views.create_post, name='post-create'),
9 | path('post//', views.post_detail, name='post-detail'),
10 | path('like/', views.like, name='post-like'),
11 | path('post//update/', PostUpdateView.as_view(), name='post-update'),
12 | path('post//delete/', views.post_delete, name='post-delete'),
13 | path('search_posts/', views.search_posts, name='search_posts'),
14 | path('user_posts/', UserPostListView.as_view(), name='user-posts'),
15 | ]
--------------------------------------------------------------------------------
/feed/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import get_object_or_404, render, redirect
2 | from django.http import HttpResponseRedirect, HttpResponse, JsonResponse
3 | from django.urls import reverse
4 | from django.contrib import messages
5 | from django.core.paginator import Paginator
6 | from django.contrib.auth.models import User
7 | from .forms import NewCommentForm, NewPostForm
8 | from django.views.generic import ListView, UpdateView, DeleteView
9 | from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
10 | from .models import Post, Comments, Like
11 | from django.contrib.auth.decorators import login_required
12 | from django.views.decorators.http import require_POST
13 | import json
14 |
15 | class PostListView(ListView):
16 | model = Post
17 | template_name = 'feed/home.html'
18 | context_object_name = 'posts'
19 | ordering = ['-date_posted']
20 | paginate_by = 10
21 | def get_context_data(self, **kwargs):
22 | context = super(PostListView, self).get_context_data(**kwargs)
23 | if self.request.user.is_authenticated:
24 | liked = [i for i in Post.objects.all() if Like.objects.filter(user = self.request.user, post=i)]
25 | context['liked_post'] = liked
26 | return context
27 |
28 | class UserPostListView(LoginRequiredMixin, ListView):
29 | model = Post
30 | template_name = 'feed/user_posts.html'
31 | context_object_name = 'posts'
32 | paginate_by = 10
33 |
34 | def get_context_data(self, **kwargs):
35 | context = super(UserPostListView, self).get_context_data(**kwargs)
36 | user = get_object_or_404(User, username=self.kwargs.get('username'))
37 | liked = [i for i in Post.objects.filter(user_name=user) if Like.objects.filter(user = self.request.user, post=i)]
38 | context['liked_post'] = liked
39 | return context
40 |
41 | def get_queryset(self):
42 | user = get_object_or_404(User, username=self.kwargs.get('username'))
43 | return Post.objects.filter(user_name=user).order_by('-date_posted')
44 |
45 |
46 | @login_required
47 | def post_detail(request, pk):
48 | post = get_object_or_404(Post, pk=pk)
49 | user = request.user
50 | is_liked = Like.objects.filter(user=user, post=post)
51 | if request.method == 'POST':
52 | form = NewCommentForm(request.POST)
53 | if form.is_valid():
54 | data = form.save(commit=False)
55 | data.post = post
56 | data.username = user
57 | data.save()
58 | return redirect('post-detail', pk=pk)
59 | else:
60 | form = NewCommentForm()
61 | return render(request, 'feed/post_detail.html', {'post':post, 'is_liked':is_liked, 'form':form})
62 |
63 | @login_required
64 | def create_post(request):
65 | user = request.user
66 | if request.method == "POST":
67 | form = NewPostForm(request.POST, request.FILES)
68 | if form.is_valid():
69 | data = form.save(commit=False)
70 | data.user_name = user
71 | data.save()
72 | messages.success(request, f'Posted Successfully')
73 | return redirect('home')
74 | else:
75 | form = NewPostForm()
76 | return render(request, 'feed/create_post.html', {'form':form})
77 |
78 | class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
79 | model = Post
80 | fields = ['description', 'pic', 'tags']
81 | template_name = 'feed/create_post.html'
82 |
83 | def form_valid(self, form):
84 | form.instance.user_name = self.request.user
85 | return super().form_valid(form)
86 |
87 | def test_func(self):
88 | post = self.get_object()
89 | if self.request.user == post.user_name:
90 | return True
91 | return False
92 |
93 | @login_required
94 | def post_delete(request, pk):
95 | post = Post.objects.get(pk=pk)
96 | if request.user== post.user_name:
97 | Post.objects.get(pk=pk).delete()
98 | return redirect('home')
99 |
100 |
101 | @login_required
102 | def search_posts(request):
103 | query = request.GET.get('p')
104 | object_list = Post.objects.filter(tags__icontains=query)
105 | liked = [i for i in object_list if Like.objects.filter(user = request.user, post=i)]
106 | context ={
107 | 'posts': object_list,
108 | 'liked_post': liked
109 | }
110 | return render(request, "feed/search_posts.html", context)
111 |
112 | @login_required
113 | def like(request):
114 | post_id = request.GET.get("likeId", "")
115 | user = request.user
116 | post = Post.objects.get(pk=post_id)
117 | liked= False
118 | like = Like.objects.filter(user=user, post=post)
119 | if like:
120 | like.delete()
121 | else:
122 | liked = True
123 | Like.objects.create(user=user, post=post)
124 | resp = {
125 | 'liked':liked
126 | }
127 | response = json.dumps(resp)
128 | return HttpResponse(response, content_type = "application/json")
129 |
130 |
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'photoshare.settings')
9 | try:
10 | from django.core.management import execute_from_command_line
11 | except ImportError as exc:
12 | raise ImportError(
13 | "Couldn't import Django. Are you sure it's installed and "
14 | "available on your PYTHONPATH environment variable? Did you "
15 | "forget to activate a virtual environment?"
16 | ) from exc
17 | execute_from_command_line(sys.argv)
18 |
19 |
20 | if __name__ == '__main__':
21 | main()
22 |
--------------------------------------------------------------------------------
/photoshare/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shubham1710/ByteWalk/9f79221304c04816521de793356a06c76809245e/photoshare/__init__.py
--------------------------------------------------------------------------------
/photoshare/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for photoshare project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'photoshare.settings')
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/photoshare/settings.py:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | import os
7 | import django_heroku
8 |
9 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
10 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
11 |
12 |
13 | # Quick-start development settings - unsuitable for production
14 | # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
15 |
16 | # SECURITY WARNING: keep the secret key used in production secret!
17 | SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
18 |
19 | # SECURITY WARNING: don't run with debug turned on in production!
20 | DEBUG = (os.environ.get('DEBUG_VALUE') == 'True')
21 |
22 | ALLOWED_HOSTS = [
23 | 'localhost',
24 | 'https://bytewalk.herokuapp.com/',
25 | 'https://bytewalk.me',
26 | 'http://bytewalk.me'
27 | ]
28 |
29 |
30 | # Application definition
31 |
32 | INSTALLED_APPS = [
33 | 'users.apps.UsersConfig',
34 | 'feed.apps.FeedConfig',
35 | 'crispy_forms',
36 | 'stdimage',
37 | 'django.contrib.admin',
38 | 'django.contrib.auth',
39 | 'django.contrib.contenttypes',
40 | 'django.contrib.sessions',
41 | 'django.contrib.messages',
42 | 'django.contrib.staticfiles',
43 | ]
44 |
45 | MIDDLEWARE = [
46 | 'django.middleware.security.SecurityMiddleware',
47 | 'django.contrib.sessions.middleware.SessionMiddleware',
48 | 'django.middleware.common.CommonMiddleware',
49 | 'django.middleware.csrf.CsrfViewMiddleware',
50 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
51 | 'django.contrib.messages.middleware.MessageMiddleware',
52 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
53 | ]
54 |
55 | ROOT_URLCONF = 'photoshare.urls'
56 |
57 | TEMPLATES = [
58 | {
59 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
60 |
61 | 'DIRS': [],
62 | 'APP_DIRS': True,
63 | 'OPTIONS': {
64 | 'context_processors': [
65 | 'django.template.context_processors.debug',
66 | 'django.template.context_processors.request',
67 | 'django.contrib.auth.context_processors.auth',
68 | 'django.contrib.messages.context_processors.messages',
69 | ],
70 | },
71 | },
72 | ]
73 |
74 | WSGI_APPLICATION = 'photoshare.wsgi.application'
75 |
76 | # Database
77 | # https://docs.djangoproject.com/en/3.0/ref/settings/#databases
78 |
79 | DATABASES = {
80 | 'default': {
81 | 'ENGINE': 'django.db.backends.sqlite3',
82 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
83 | }
84 | }
85 |
86 |
87 | # Password validation
88 | # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
89 |
90 | AUTH_PASSWORD_VALIDATORS = [
91 | {
92 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
93 | },
94 | {
95 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
96 | },
97 | {
98 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
99 | },
100 | {
101 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
102 | },
103 | ]
104 |
105 |
106 | # Internationalization
107 | # https://docs.djangoproject.com/en/3.0/topics/i18n/
108 |
109 | LANGUAGE_CODE = 'en-us'
110 |
111 | TIME_ZONE = 'UTC'
112 |
113 | USE_I18N = True
114 |
115 | USE_L10N = True
116 |
117 | USE_TZ = True
118 |
119 | # Static files (CSS, JavaScript, Images)
120 | # https://docs.djangoproject.com/en/3.0/howto/static-files/
121 | STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
122 | STATIC_URL = '/static/'
123 |
124 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
125 | MEDIA_URL = '/media/'
126 |
127 | CRISPY_TEMPLATE_PACK = 'bootstrap4'
128 |
129 | LOGIN_REDIRECT_URL = 'home'
130 | LOGIN_URL = 'login'
131 |
132 | EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
133 | EMAIL_HOST = 'smtp.gmail.com'
134 | EMAIL_PORT = 587
135 | EMAIL_USE_TLS = True
136 | EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
137 | EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')
138 |
139 | DEFAULT_FILE_STORAGE = 'django_gcloud_storage.DjangoGCloudStorage'
140 |
141 | GCS_PROJECT = os.environ.get('GCS_PROJECT')
142 | GCS_BUCKET = os.environ.get('GCS_BUCKET')
143 | GCS_CREDENTIALS_FILE_PATH = os.path.join(BASE_DIR, "my-key.json")
144 | GCS_USE_UNSIGNED_URLS = True
145 |
146 | django_heroku.settings(locals())
147 |
--------------------------------------------------------------------------------
/photoshare/urls.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.urls import path, include
3 | from users import views as user_views
4 | from django.contrib.auth import views as auth_views
5 | from django.conf.urls.static import static
6 | from django.conf import settings
7 |
8 | urlpatterns = [
9 | path('admin/', admin.site.urls),
10 | path('', include('feed.urls')),
11 | path('users/', user_views.users_list, name='users_list'),
12 | path('users//', user_views.profile_view, name='profile_view'),
13 | path('friends/', user_views.friend_list, name='friend_list'),
14 | path('users/friend-request/send//', user_views.send_friend_request, name='send_friend_request'),
15 | path('users/friend-request/cancel//', user_views.cancel_friend_request, name='cancel_friend_request'),
16 | path('users/friend-request/accept//', user_views.accept_friend_request, name='accept_friend_request'),
17 | path('users/friend-request/delete//', user_views.delete_friend_request, name='delete_friend_request'),
18 | path('users/friend/delete//', user_views.delete_friend, name='delete_friend'),
19 | path('edit-profile/', user_views.edit_profile, name='edit_profile'),
20 | path('my-profile/', user_views.my_profile, name='my_profile'),
21 | path('search_users/', user_views.search_users, name='search_users'),
22 | path('register/', user_views.register, name='register'),
23 | path('login/', auth_views.LoginView.as_view(template_name='users/login.html'), name='login'),
24 | path('logout/', auth_views.LogoutView.as_view(template_name='users/logout.html'), name='logout'),
25 | path('password-reset/', auth_views.PasswordResetView.as_view(template_name='users/password_reset.html'), name='password_reset'),
26 | path('password-reset/done/', auth_views.PasswordResetView.as_view(template_name='users/password_reset_done.html'), name='password_reset_done'),
27 | path('password-reset-confirm///', auth_views.PasswordResetConfirmView.as_view(template_name='users/password_reset_confirm.html'), name='password_reset_confirm'),
28 | path('password-reset-complete/', auth_views.PasswordResetCompleteView.as_view(template_name='users/password_reset_complete.html'), name='password_reset_complete'),
29 | ]
30 |
31 | if settings.DEBUG:
32 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
33 |
34 |
--------------------------------------------------------------------------------
/photoshare/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for photoshare project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'photoshare.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | boto3==1.9.96
2 | botocore==1.12.96
3 | certifi==2018.10.15
4 | chardet==3.0.4
5 | dj-database-url==0.5.0
6 | Django==2.1
7 | django-gcloud-storage==0.4.0
8 | django-stdimage==5.1.1
9 | django-crispy-forms==1.7.2
10 | django-autoslug==1.9.8
11 | django-heroku==0.3.1
12 | django-storages==1.7.1
13 | docutils==0.14
14 | google-api-core==1.22.1
15 | google-auth==1.19.2
16 | google-auth-oauthlib==0.4.1
17 | google-cloud-core==1.4.1
18 | google-cloud-storage==1.30.0
19 | google-crc32c==0.1.0
20 | google-pasta==0.2.0
21 | google-resumable-media==0.7.1
22 | googleapis-common-protos==1.52.0
23 | gTTS==2.1.1
24 | gTTS-token==1.1.3
25 | gunicorn==19.9.0
26 | idna==2.7
27 | imagesize==1.1.0
28 | jmespath==0.9.3
29 | Pillow==5.2.0
30 | psycopg2==2.7.7
31 | python-dateutil==2.8.0
32 | pytz==2018.5
33 | requests==2.19.1
34 | s3transfer==0.2.0
35 | six==1.12.0
36 | urllib3==1.23
37 | whitenoise==4.1.2
38 |
--------------------------------------------------------------------------------
/users/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shubham1710/ByteWalk/9f79221304c04816521de793356a06c76809245e/users/__init__.py
--------------------------------------------------------------------------------
/users/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import Profile, FriendRequest
3 |
4 | admin.site.register(Profile)
5 | admin.site.register(FriendRequest)
6 |
--------------------------------------------------------------------------------
/users/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class UsersConfig(AppConfig):
5 | name = 'users'
6 |
--------------------------------------------------------------------------------
/users/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.contrib.auth.models import User
3 | from django.contrib.auth.forms import UserCreationForm
4 | from .models import Profile
5 |
6 | class UserRegisterForm(UserCreationForm):
7 | email = forms.EmailField()
8 |
9 | class Meta:
10 | model = User
11 | fields = ['username', 'email', 'password1', 'password2']
12 |
13 |
14 | class UserUpdateForm(forms.ModelForm):
15 | email = forms.EmailField()
16 |
17 | class Meta:
18 | model = User
19 | fields = ['username', 'email']
20 |
21 | class ProfileUpdateForm(forms.ModelForm):
22 | class Meta:
23 | model = Profile
24 | fields = ['bio', 'image']
--------------------------------------------------------------------------------
/users/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-11 11:19
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | initial = True
11 |
12 | dependencies = [
13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
14 | ]
15 |
16 | operations = [
17 | migrations.CreateModel(
18 | name='Profile',
19 | fields=[
20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21 | ('name', models.CharField(blank=True, max_length=100)),
22 | ('image', models.ImageField(default='default.png', upload_to='profile_pics')),
23 | ('slug', models.SlugField(unique=True)),
24 | ('bio', models.CharField(blank=True, max_length=255)),
25 | ('friends', models.ManyToManyField(blank=True, to='users.Profile')),
26 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
27 | ],
28 | ),
29 | migrations.CreateModel(
30 | name='FriendRequest',
31 | fields=[
32 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
33 | ('timestamp', models.DateTimeField(auto_now_add=True)),
34 | ('from_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='from_user', to=settings.AUTH_USER_MODEL)),
35 | ('to_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='to_user', to=settings.AUTH_USER_MODEL)),
36 | ],
37 | ),
38 | ]
39 |
--------------------------------------------------------------------------------
/users/migrations/0002_auto_20200811_1941.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-11 14:11
2 |
3 | import autoslug.fields
4 | from django.db import migrations
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('users', '0001_initial'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='profile',
16 | name='slug',
17 | field=autoslug.fields.AutoSlugField(editable=False, populate_from='user.username'),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/users/migrations/0003_auto_20200811_1943.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-11 14:13
2 |
3 | import autoslug.fields
4 | from django.db import migrations
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('users', '0002_auto_20200811_1941'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='profile',
16 | name='slug',
17 | field=autoslug.fields.AutoSlugField(editable=False, populate_from='user'),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/users/migrations/0004_auto_20200812_0907.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-12 03:37
2 |
3 | from django.db import migrations
4 | import stdimage.models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('users', '0003_auto_20200811_1943'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='profile',
16 | name='image',
17 | field=stdimage.models.JPEGField(default='default.png', upload_to='profile_pics'),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/users/migrations/0005_remove_profile_name.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-13 14:24
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('users', '0004_auto_20200812_0907'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='profile',
15 | name='name',
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/users/migrations/0006_auto_20200815_0840.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.8 on 2020-08-15 03:10
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('users', '0005_remove_profile_name'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='profile',
15 | name='image',
16 | field=models.ImageField(default='default.png', upload_to='profile_pics'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/users/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shubham1710/ByteWalk/9f79221304c04816521de793356a06c76809245e/users/migrations/__init__.py
--------------------------------------------------------------------------------
/users/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 | from django.urls import reverse
4 | from django.utils import timezone
5 | from django.db.models.signals import post_save
6 | from django.conf import settings
7 | from autoslug import AutoSlugField
8 |
9 | class Profile(models.Model):
10 | user = models.OneToOneField(User, on_delete=models.CASCADE)
11 | image = models.ImageField(default='default.png', upload_to='profile_pics')
12 | slug = AutoSlugField(populate_from='user')
13 | bio = models.CharField(max_length=255, blank=True)
14 | friends = models.ManyToManyField("Profile", blank=True)
15 |
16 | def __str__(self):
17 | return str(self.user.username)
18 |
19 | def get_absolute_url(self):
20 | return "/users/{}".format(self.slug)
21 |
22 | def post_save_user_model_receiver(sender, instance, created, *args, **kwargs):
23 | if created:
24 | try:
25 | Profile.objects.create(user=instance)
26 | except:
27 | pass
28 |
29 | post_save.connect(post_save_user_model_receiver, sender=settings.AUTH_USER_MODEL)
30 |
31 | class FriendRequest(models.Model):
32 | to_user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='to_user', on_delete=models.CASCADE)
33 | from_user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='from_user', on_delete=models.CASCADE)
34 | timestamp = models.DateTimeField(auto_now_add=True)
35 |
36 | def __str__(self):
37 | return "From {}, to {}".format(self.from_user.username, self.to_user.username)
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/users/templates/users/edit_profile.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% block searchform %}
2 |
12 | {% endblock searchform %} {% load crispy_forms_tags %} {% block content %}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
Update Profile
20 |
40 |
41 |
42 |
43 |
44 |
45 | {% endblock content %}
46 |
--------------------------------------------------------------------------------
/users/templates/users/friend_list.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% block searchform %}
2 |
12 | {% endblock searchform %} {% block content %}
13 |
14 |
15 |
16 | {% if friends %}
17 |
18 |
19 | {% for user_p in friends %}
20 |

28 |
{{ user_p }}
31 |
UnFriend
38 |
39 | {% endfor %}
40 |
41 |
42 | {% else %}
43 |
44 | You have no friends now! Make some new
46 | friends here!
48 |
49 | {% endif %}
50 |
51 |
52 |
53 |

59 |
60 |
{{ request.user }}
61 |
62 | {{ request.user.profile.friends.count }}
63 |
Friends
64 |
65 |
{{ request.user.profile.bio }}
66 |
67 |
68 |
69 |
70 | {% endblock content %}
71 |
72 |
--------------------------------------------------------------------------------
/users/templates/users/login.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% load crispy_forms_tags %}
2 | {% block content %}
3 |
4 |
41 | {% endblock content %}
42 |
--------------------------------------------------------------------------------
/users/templates/users/logout.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% block content %}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | You have been logged out!
10 |
11 |
20 |
21 |
22 |
23 |
24 |
25 | {% endblock content %}
26 |
--------------------------------------------------------------------------------
/users/templates/users/password_reset.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% load crispy_forms_tags %}
2 | {% block content %}
3 |
4 |
5 |
6 |
7 |
8 |
9 |
Reset Password
10 |
25 |
26 |
27 |
28 |
29 |
30 | {% endblock content %}
31 |
--------------------------------------------------------------------------------
/users/templates/users/password_reset_complete.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% block content %}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Your new password has been set.
10 |
11 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {% endblock content %}
27 |
--------------------------------------------------------------------------------
/users/templates/users/password_reset_confirm.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load crispy_forms_tags %} {% load static %}
2 | {% block content %}
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
Reset Password
11 |
26 |
27 |
28 |
29 |
30 |
31 | {% endblock content %}
32 |
--------------------------------------------------------------------------------
/users/templates/users/password_reset_done.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% block content %}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | An email has been sent with instructions to reset your
11 | password.
13 |
14 |
23 |
24 |
25 |
26 |
27 |
28 | {% endblock content %}
29 |
--------------------------------------------------------------------------------
/users/templates/users/profile.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% block searchform %}
2 |
12 | {% endblock searchform %} {% block content %}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
33 |
36 |
37 |
38 |
39 |
42 |
43 |
{{ u.profile.friends.count }}
44 | {% if request.user == u %}
45 |
Friends
48 | {% else %}
49 |
Friends
50 | {% endif %}
51 |
{{ post_count }}
52 |
Posts
57 |
58 |
59 |
60 |
61 |
62 |
{{ u }}
63 |
{{ u.profile.bio }}
64 |
65 | {% if request.user == u %}
66 |
Edit Profile
69 | {% else %} {% if button_status == 'not_friend' %}
70 |
Add Friend
77 | {% elif button_status == 'friend_request_sent' %}
78 |
Cancel Request
85 | {% elif button_status == 'friend_request_received' %}
86 |
Accept Request
93 |
Reject Request
100 | {% else %}
101 |
UnFriend
108 | {% endif %} {% endif %}
109 |
110 |
111 |
112 |
113 |
114 |
115 | {% if request.user == u %}
116 |
117 |
118 |
119 |
120 |
Friend Requests Sent ({{ sent_friend_requests.count }})
121 |
122 | {% if not sent_friend_requests %}
123 |
No sent requests!
124 | {% else %} {% for s_request in sent_friend_requests %}
125 |

133 |
{{ s_request.to_user.username }}
136 |
137 | Cancel
142 |
143 |
144 | {% endfor %} {% endif %}
145 |
146 |
147 |
148 |
149 |
150 |
151 |
Friend Requests Recieved ({{ rec_friend_requests.count }})
152 |
153 | {% if not rec_friend_requests %}
154 |
No recieved requests!
155 | {% else %} {% for r_request in rec_friend_requests %}
156 |

164 |
{{ r_request.from_user.username }}
168 |
169 |
170 | Accept
175 | Reject
180 |
181 |
182 | {% endfor %} {% endif %}
183 |
184 |
185 |
186 |
187 | {% endif %}
188 |
189 |
190 |
191 | {% endblock content %}
192 |
--------------------------------------------------------------------------------
/users/templates/users/register.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load crispy_forms_tags %} {% load static %}
2 | {% block content %}
3 |
4 |
36 | {% endblock content %}
37 |
--------------------------------------------------------------------------------
/users/templates/users/search_users.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %} {% block searchform %}
2 |
12 | {% endblock searchform %} {% block content %}
13 |
14 |
15 |
16 | {% if not users %}
17 |
18 |
No such users found!
19 | {% else %}
20 |
21 |
22 | {% for user_p in users %}
23 |

31 |
{{ user_p }}
34 |
35 | {% endfor %}
36 |
37 |
38 | {% endif %}
39 |
40 |
41 |
42 |

48 |
49 |
{{ request.user }}
50 |
51 | {{ request.user.profile.friends.count }}
52 |
Friends
53 |
54 |
{{ request.user.profile.bio }}
55 |
56 |
57 |
58 |
59 | {% endblock content %}
60 |
61 |
--------------------------------------------------------------------------------
/users/templates/users/users_list.html:
--------------------------------------------------------------------------------
1 | {% extends "feed/layout.html" %} {% load static %}{% block searchform %}
2 |
12 | {% endblock searchform %} {% block content %}
13 |
14 |
15 |
16 | {% if users %}
17 |
18 |
19 | {% for user_p in users %}
20 |

28 |
{{ user_p }}
31 | {% if not user_p.user in sent %}
32 |
33 | Add Friend
38 |
39 | {% else %}
40 |
41 | Cancel Request
46 |
47 | {% endif %}
48 |
49 | {% endfor %}
50 |
51 |
52 | {% else %}
53 |
No new people to add now! Please come back later!
54 | {% endif %}
55 |
56 |
57 |
58 |

64 |
65 |
{{ request.user }}
66 |
67 | {{ request.user.profile.friends.count }}
68 |
Friends
69 |
70 |
{{ request.user.profile.bio }}
71 |
72 |
73 |
74 |
75 | {% endblock content %}
76 |
77 |
--------------------------------------------------------------------------------
/users/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/users/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render, redirect, get_object_or_404
2 | from .models import Profile
3 | from feed.models import Post
4 | from django.contrib import messages
5 | from django.contrib.auth.decorators import login_required
6 | from django.contrib.auth import get_user_model
7 | from django.conf import settings
8 | from django.http import HttpResponseRedirect
9 | from .models import Profile, FriendRequest
10 | from .forms import UserRegisterForm, UserUpdateForm, ProfileUpdateForm
11 | import random
12 |
13 | User = get_user_model()
14 |
15 | @login_required
16 | def users_list(request):
17 | users = Profile.objects.exclude(user=request.user)
18 | sent_friend_requests = FriendRequest.objects.filter(from_user=request.user)
19 | my_friends = request.user.profile.friends.all()
20 | sent_to = []
21 | friends = []
22 | for user in my_friends:
23 | friend = user.friends.all()
24 | for f in friend:
25 | if f in friends:
26 | friend = friend.exclude(user=f.user)
27 | friends += friend
28 | for i in my_friends:
29 | if i in friends:
30 | friends.remove(i)
31 | if request.user.profile in friends:
32 | friends.remove(request.user.profile)
33 | random_list = random.sample(list(users), min(len(list(users)), 10))
34 | for r in random_list:
35 | if r in friends:
36 | random_list.remove(r)
37 | friends += random_list
38 | for i in my_friends:
39 | if i in friends:
40 | friends.remove(i)
41 | for se in sent_friend_requests:
42 | sent_to.append(se.to_user)
43 | context = {
44 | 'users': friends,
45 | 'sent': sent_to
46 | }
47 | return render(request, "users/users_list.html", context)
48 |
49 | def friend_list(request):
50 | p = request.user.profile
51 | friends = p.friends.all()
52 | context={
53 | 'friends': friends
54 | }
55 | return render(request, "users/friend_list.html", context)
56 |
57 | @login_required
58 | def send_friend_request(request, id):
59 | user = get_object_or_404(User, id=id)
60 | frequest, created = FriendRequest.objects.get_or_create(
61 | from_user=request.user,
62 | to_user=user)
63 | return HttpResponseRedirect('/users/{}'.format(user.profile.slug))
64 |
65 | @login_required
66 | def cancel_friend_request(request, id):
67 | user = get_object_or_404(User, id=id)
68 | frequest = FriendRequest.objects.filter(
69 | from_user=request.user,
70 | to_user=user).first()
71 | frequest.delete()
72 | return HttpResponseRedirect('/users/{}'.format(user.profile.slug))
73 |
74 | @login_required
75 | def accept_friend_request(request, id):
76 | from_user = get_object_or_404(User, id=id)
77 | frequest = FriendRequest.objects.filter(from_user=from_user, to_user=request.user).first()
78 | user1 = frequest.to_user
79 | user2 = from_user
80 | user1.profile.friends.add(user2.profile)
81 | user2.profile.friends.add(user1.profile)
82 | if(FriendRequest.objects.filter(from_user=request.user, to_user=from_user).first()):
83 | request_rev = FriendRequest.objects.filter(from_user=request.user, to_user=from_user).first()
84 | request_rev.delete()
85 | frequest.delete()
86 | return HttpResponseRedirect('/users/{}'.format(request.user.profile.slug))
87 |
88 | @login_required
89 | def delete_friend_request(request, id):
90 | from_user = get_object_or_404(User, id=id)
91 | frequest = FriendRequest.objects.filter(from_user=from_user, to_user=request.user).first()
92 | frequest.delete()
93 | return HttpResponseRedirect('/users/{}'.format(request.user.profile.slug))
94 |
95 | def delete_friend(request, id):
96 | user_profile = request.user.profile
97 | friend_profile = get_object_or_404(Profile, id=id)
98 | user_profile.friends.remove(friend_profile)
99 | friend_profile.friends.remove(user_profile)
100 | return HttpResponseRedirect('/users/{}'.format(friend_profile.slug))
101 |
102 | @login_required
103 | def profile_view(request, slug):
104 | p = Profile.objects.filter(slug=slug).first()
105 | u = p.user
106 | sent_friend_requests = FriendRequest.objects.filter(from_user=p.user)
107 | rec_friend_requests = FriendRequest.objects.filter(to_user=p.user)
108 | user_posts = Post.objects.filter(user_name=u)
109 |
110 | friends = p.friends.all()
111 |
112 | # is this user our friend
113 | button_status = 'none'
114 | if p not in request.user.profile.friends.all():
115 | button_status = 'not_friend'
116 |
117 | # if we have sent him a friend request
118 | if len(FriendRequest.objects.filter(
119 | from_user=request.user).filter(to_user=p.user)) == 1:
120 | button_status = 'friend_request_sent'
121 |
122 | # if we have recieved a friend request
123 | if len(FriendRequest.objects.filter(
124 | from_user=p.user).filter(to_user=request.user)) == 1:
125 | button_status = 'friend_request_received'
126 |
127 | context = {
128 | 'u': u,
129 | 'button_status': button_status,
130 | 'friends_list': friends,
131 | 'sent_friend_requests': sent_friend_requests,
132 | 'rec_friend_requests': rec_friend_requests,
133 | 'post_count': user_posts.count
134 | }
135 |
136 | return render(request, "users/profile.html", context)
137 |
138 | def register(request):
139 | if request.method == 'POST':
140 | form = UserRegisterForm(request.POST)
141 | if form.is_valid():
142 | form.save()
143 | username = form.cleaned_data.get('username')
144 | messages.success(request, f'Your account has been created! You can now login!')
145 | return redirect('login')
146 | else:
147 | form = UserRegisterForm()
148 | return render(request, 'users/register.html', {'form':form})
149 |
150 | @login_required
151 | def edit_profile(request):
152 | if request.method == 'POST':
153 | u_form = UserUpdateForm(request.POST, instance=request.user)
154 | p_form = ProfileUpdateForm(request.POST, request.FILES, instance=request.user.profile)
155 | if u_form.is_valid() and p_form.is_valid():
156 | u_form.save()
157 | p_form.save()
158 | messages.success(request, f'Your account has been updated!')
159 | return redirect('my_profile')
160 | else:
161 | u_form = UserUpdateForm(instance=request.user)
162 | p_form = ProfileUpdateForm(instance=request.user.profile)
163 | context ={
164 | 'u_form': u_form,
165 | 'p_form': p_form,
166 | }
167 | return render(request, 'users/edit_profile.html', context)
168 |
169 | @login_required
170 | def my_profile(request):
171 | p = request.user.profile
172 | you = p.user
173 | sent_friend_requests = FriendRequest.objects.filter(from_user=you)
174 | rec_friend_requests = FriendRequest.objects.filter(to_user=you)
175 | user_posts = Post.objects.filter(user_name=you)
176 | friends = p.friends.all()
177 |
178 | # is this user our friend
179 | button_status = 'none'
180 | if p not in request.user.profile.friends.all():
181 | button_status = 'not_friend'
182 |
183 | # if we have sent him a friend request
184 | if len(FriendRequest.objects.filter(
185 | from_user=request.user).filter(to_user=you)) == 1:
186 | button_status = 'friend_request_sent'
187 |
188 | if len(FriendRequest.objects.filter(
189 | from_user=p.user).filter(to_user=request.user)) == 1:
190 | button_status = 'friend_request_received'
191 |
192 | context = {
193 | 'u': you,
194 | 'button_status': button_status,
195 | 'friends_list': friends,
196 | 'sent_friend_requests': sent_friend_requests,
197 | 'rec_friend_requests': rec_friend_requests,
198 | 'post_count': user_posts.count
199 | }
200 |
201 | return render(request, "users/profile.html", context)
202 |
203 | @login_required
204 | def search_users(request):
205 | query = request.GET.get('q')
206 | object_list = User.objects.filter(username__icontains=query)
207 | context ={
208 | 'users': object_list
209 | }
210 | return render(request, "users/search_users.html", context)
211 |
--------------------------------------------------------------------------------