#books/urls.py
from django.urls import path
app_name = 'books'
urlpatterns = [
]
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('books.urls', namespace='books'))
]
- Next we create a models for book:
from django.contrib.auth.models import User
from django.db import models
from taggit.managers import TaggableManager
from cloudinary.models import CloudinaryField
class Book(models.Model):
title = models.CharField(max_length=255)
image = CloudinaryField('image')
description = models.CharField(max_length=1000)
author = models.ForeignKey(User, on_delete=models.CASCADE)
tags = TaggableManager()
updated = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
#books/views.py
from django.shortcuts import get_object_or_404, render, redirect
from taggit.models import Tag
from books.models import Book
from .forms import BookForm
def home(request, pk=None):
tag_obj = None
if not pk:
books = Book.objects.all()
else:
tag_obj = get_object_or_404(Tag, pk=pk)
books = Book.objects.filter(tags__in=[tag_obj])
return render(request, 'home.html', {
'tag': tag_obj,
'books': books
})
def create(request, *args, **kwargs):
if request.method == 'POST':
form = BookForm(request.POST, request.FILES)
if form.is_valid():
book = form.save(commit=False)
book.author = request.user
form.save()
return redirect('books:home')
else:
form = BookForm()
return render(request, 'create.html', {
"form": form
})
#books/urls.py
from django.urls import path
from .views import home, create
app_name = 'books'
urlpatterns = [
path('', home, name='home'),
path('tag/<str:pk>', home, name="books_tag"),
path('create', create, name="create" )
]
- Now we setup templates and config cloudinary api in settings
Template Setup:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hello Bulma!</title>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<script defer
src="https://use.fontawesome.com/releases/v5.14.0/js/all.js"></script>
<style type="text/css" media="screen">
body {
display: flex;
min-height: 100vh;
flex-direction: column;
background-color: info
}
#wrapper {
flex: 1;
}
</style>
</head>
<body class="has-background-light">
{% include 'nav.html' %}
<section class="section">
<div class="container is-link">
{% block content %}
{% endblock content %}
</div>
</section>
</body>
</html>
{% extends 'base.html' %}
{% load cloudinary %}
{% block content %}
<h1 class="title">Tag-Me</h1>
<p>Books tagged with <strong>{{ tag.name }}</strong></p>
<div class="columns is-mobile mt-5">
<div class="column is-half is-offset-one-quarter">
{% for book in books %}
<div class="card mt-3">
<div class="card-content">
<div class="media">
<div class="media-left">
<figure class="image is-128x128">
<img
src={{book.image.url}}
alt="Placeholder image"
/>
</figure>
</div>
<div class="media-content">
{% if book.author%}
<p class="title is-4">{{ book.author }}</p>
<p class="subtitle">{{ book.title }}</p>
{% endif %}
</div>
</div>
<div class="content">
{{ book.description }} {% if book.tags %}
<div class="tags">
{% for tag in book.tags.all %}
<a href="{% url 'books:books_tag' tag.pk %}">{{ tag }}
</a>
{% endfor %}
</div>
{% endif %}
<br />
<time datetime="2016-1-1"
><p>{{ book.updated|date:"M j, Y" }}</p></time
>
</div>
</div>
</div>
<p></p>
{% endfor %}
</div>
</div>
{% endblock content %}
{% extends 'base.html' %} {% block content %}
<div class="columns is-mobile mt-5">
<div class="column is-half is-offset-one-quarter ">
<form
action="{% url 'books:create' %}"
method="post"
enctype="multipart/form-data"
>
<p class="title">CREATE BOOK</p>
<div class="box">
{% csrf_token %}
<div class="field">
<label class="label">Title</label>
<div class="control">
<input
class="input"
type="text"
name="title"
placeholder="Book Title"
/>
</div>
{% for err in form.title.errors %}
<small class="is-danger">{{err}}</small>
{% endfor %}
</div>
<div class="field">
<label class="label">Description</label>
<div class="control">
<input
class="input"
name="description"
type="text"
placeholder="Book Descriptions"
/>
</div>
{% for err in form.description.errors %}
<small class="is-danger">{{err}}</small>
{% endfor %}
</div>
<div class="field">
<label class="label">Tags</label>
<div class="control">
<input
class="input"
type="text"
name="tags"
placeholder="Book Tags"
/>
</div>
{% for err in form.tags.errors %}
<small class="is-danger">{{err}}</small>
{% endfor %}
</div>
<div class="field">
<div class="file is-centered is-boxed is-success has-name">
<label class="file-label">
<input class="file-input" type="file" name="image" />
<span class="file-cta">
<span class="file-icon">
<i class="fas fa-upload"></i>
</span>
<span class="file-label"> Image Upload </span>
</span>
<span class="file-name">
</span>
</label>
</div>
</div>
<div class="control">
<button class="button is-primary" type="submit">Submit</button>
</div>
</div>
</form>
</div>
</div>
{% endblock content %}
<nav class="navbar is-info" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="https://bulma.io">
<img src="https://bulma.io/images/bulma-logo.png" width="112"
height="28">
</a>
<a role="button" class="navbar-burger" aria-label="menu"
aria-expanded="false" data-target="navbarBasicExample">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" href={% url 'books:home' %}>
Home
</a>
<a class="navbar-item" href={% url 'books:create' %}>
Create
</a>
<a class="navbar-item">
</a>
</div>
</div>
</div>
</nav>
Still there are lots of improve can be done like update, delete , permission, login, signup so its your turn guys to make it happen. I won't be doing here ...
- Here, is link for this app demo : Tagme
- You can access amin panel to but access are limit:
- username: heroku
- password: hero@123
|
Create page
|