Browse Source

Libros en Django

politica
Celestino Rey 1 year ago
parent
commit
ca3215411c
46 changed files with 544 additions and 338 deletions
  1. +0
    -3
      Django/README.md
  2. +0
    -22
      Django/mysite/polls/admin.py
  3. +0
    -32
      Django/mysite/polls/migrations/0001_initial.py
  4. +0
    -34
      Django/mysite/polls/models.py
  5. BIN
      Django/mysite/polls/static/polls/images/background.jpeg
  6. +0
    -8
      Django/mysite/polls/static/polls/style.css
  7. +0
    -12
      Django/mysite/polls/templates/polls/detail.html
  8. +0
    -13
      Django/mysite/polls/templates/polls/index.html
  9. +0
    -9
      Django/mysite/polls/templates/polls/results.html
  10. +0
    -102
      Django/mysite/polls/tests.py
  11. +0
    -11
      Django/mysite/polls/urls.py
  12. +0
    -62
      Django/mysite/polls/views.py
  13. +0
    -12
      Django/mysite/templates/admin/base_site.html
  14. +16
    -0
      Libros/Pipfile
  15. +172
    -0
      Libros/Pipfile.lock
  16. BIN
      Libros/biblioteca/autores/auster.jpg
  17. BIN
      Libros/biblioteca/autores/daniel.jpg
  18. +0
    -0
      Libros/biblioteca/biblioteca/__init__.py
  19. +2
    -2
      Libros/biblioteca/biblioteca/asgi.py
  20. +8
    -8
      Libros/biblioteca/biblioteca/settings.py
  21. +9
    -3
      Libros/biblioteca/biblioteca/urls.py
  22. +2
    -2
      Libros/biblioteca/biblioteca/wsgi.py
  23. BIN
      Libros/biblioteca/db.sqlite3
  24. +0
    -0
      Libros/biblioteca/gestion/__init__.py
  25. +9
    -0
      Libros/biblioteca/gestion/admin.py
  26. +2
    -2
      Libros/biblioteca/gestion/apps.py
  27. +12
    -0
      Libros/biblioteca/gestion/forms.py
  28. +34
    -0
      Libros/biblioteca/gestion/migrations/0001_initial.py
  29. +20
    -0
      Libros/biblioteca/gestion/migrations/0002_alter_libro_fecha_publicacion.py
  30. +18
    -0
      Libros/biblioteca/gestion/migrations/0003_libro_portada.py
  31. +18
    -0
      Libros/biblioteca/gestion/migrations/0004_autor_foto.py
  32. +0
    -0
      Libros/biblioteca/gestion/migrations/__init__.py
  33. +29
    -0
      Libros/biblioteca/gestion/models.py
  34. +14
    -0
      Libros/biblioteca/gestion/templates/gestion/detalle_autor.html
  35. +18
    -0
      Libros/biblioteca/gestion/templates/gestion/detalle_libro.html
  36. +15
    -0
      Libros/biblioteca/gestion/templates/gestion/form_autor.html
  37. +15
    -0
      Libros/biblioteca/gestion/templates/gestion/form_libro.html
  38. +19
    -0
      Libros/biblioteca/gestion/templates/gestion/lista_autores.html
  39. +19
    -0
      Libros/biblioteca/gestion/templates/gestion/lista_libros.html
  40. +3
    -0
      Libros/biblioteca/gestion/tests.py
  41. +16
    -0
      Libros/biblioteca/gestion/urls.py
  42. +73
    -0
      Libros/biblioteca/gestion/views.py
  43. BIN
      Libros/biblioteca/libros/Invisible_-_Paul_Auster_5.epub
  44. BIN
      Libros/biblioteca/libros/Sunset_Park_-_Paul_Auster_12.epub
  45. +1
    -1
      Libros/biblioteca/manage.py
  46. BIN
      Libros/biblioteca/portadas/sunsetpark.jpeg

+ 0
- 3
Django/README.md View File

@ -1,3 +0,0 @@
# Fuente usada
https://docs.djangoproject.com/en/5.0/intro/tutorial01/

+ 0
- 22
Django/mysite/polls/admin.py View File

@ -1,22 +0,0 @@
from django.contrib import admin
from .models import Choice, Question
class ChoiceInline(admin.TabularInline):
model = Choice
extra = 3
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {"fields": ["question_text"]}),
("Date information", {"fields": ["pub_date"], "classes": ["collapse"]}),
]
inlines = [ChoiceInline]
list_display = ["question_text", "pub_date", "was_published_recently"]
list_filter = ["pub_date"]
search_fields = ["question_text"]
admin.site.register(Question, QuestionAdmin)

+ 0
- 32
Django/mysite/polls/migrations/0001_initial.py View File

@ -1,32 +0,0 @@
# Generated by Django 5.0.7 on 2024-07-18 08:36
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Question',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('question_text', models.CharField(max_length=200)),
('pub_date', models.DateTimeField(verbose_name='date published')),
],
),
migrations.CreateModel(
name='Choice',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('choice_text', models.CharField(max_length=200)),
('votes', models.IntegerField(default=0)),
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.question')),
],
),
]

+ 0
- 34
Django/mysite/polls/models.py View File

@ -1,34 +0,0 @@
import datetime
from django.db import models
from django.utils import timezone
from django.contrib import admin
# Create your models here.
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField("date published")
def __str__(self):
return self.question_text
@admin.display(
boolean=True,
ordering="pub_date",
description="Published recently?",
)
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text

BIN
Django/mysite/polls/static/polls/images/background.jpeg View File

Before After
Width: 6000  |  Height: 4000  |  Size: 5.3 MiB

+ 0
- 8
Django/mysite/polls/static/polls/style.css View File

@ -1,8 +0,0 @@
li a {
color: green;
}
body {
background: white url("images/background.jpeg") no-repeat;
background-size: 50% 50%;
}

+ 0
- 12
Django/mysite/polls/templates/polls/detail.html View File

@ -1,12 +0,0 @@
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
<fieldset>
<legend><h1>{{ question.question_text }}</h1></legend>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
{% for choice in question.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
</fieldset>
<input type="submit" value="Vote">
</form>

+ 0
- 13
Django/mysite/polls/templates/polls/index.html View File

@ -1,13 +0,0 @@
{% load static %}
<link rel="stylesheet" href="{% static 'polls/style.css' %}">
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}

+ 0
- 9
Django/mysite/polls/templates/polls/results.html View File

@ -1,9 +0,0 @@
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
<a href="{% url 'polls:detail' question.id %}">Vote again?</a>

+ 0
- 102
Django/mysite/polls/tests.py View File

@ -1,102 +0,0 @@
import datetime
from django.test import TestCase
from django.utils import timezone
from django.urls import reverse
from .models import Question
def create_question(question_text, days):
"""
Create a question with the given `question_text` and published the
given number of `days` offset to now (negative for questions published
in the past, positive for questions that have yet to be published).
"""
time = timezone.now() + datetime.timedelta(days=days)
return Question.objects.create(question_text=question_text, pub_date=time)
class QuestionModelTests(TestCase):
def test_was_published_recently_with_future_question(self):
"""
was_published_recently() returns False for questions whose pub_date
is in the future.
"""
time = timezone.now() + datetime.timedelta(days=30)
future_question = Question(pub_date=time)
self.assertIs(future_question.was_published_recently(), False)
def test_was_published_recently_with_old_question(self):
"""
was_published_recently() returns False for questions whose pub_date
is older than 1 day.
"""
time = timezone.now() - datetime.timedelta(days=1, seconds=1)
old_question = Question(pub_date=time)
self.assertIs(old_question.was_published_recently(), False)
def test_was_published_recently_with_recent_question(self):
"""
was_published_recently() returns True for questions whose pub_date
is within the last day.
"""
time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
recent_question = Question(pub_date=time)
self.assertIs(recent_question.was_published_recently(), True)
class QuestionIndexViewTests(TestCase):
def test_no_questions(self):
"""
If no questions exist, an appropriate message is displayed.
"""
response = self.client.get(reverse("polls:index"))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "No polls are available.")
self.assertQuerySetEqual(response.context["latest_question_list"], [])
def test_future_question(self):
"""
The detail view of a question with a pub_date in the future
returns a 404 not found.
"""
future_question = create_question(question_text="Future question.", days=5)
url = reverse("polls:detail", args=(future_question.id,))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_past_question(self):
"""
The detail view of a question with a pub_date in the past
displays the question's text.
"""
past_question = create_question(question_text="Past Question.", days=-5)
url = reverse("polls:detail", args=(past_question.id,))
response = self.client.get(url)
self.assertContains(response, past_question.question_text)
def test_future_question_and_past_question(self):
"""
Even if both past and future questions exist, only past questions
are displayed.
"""
question = create_question(question_text="Past question.", days=-30)
create_question(question_text="Future question.", days=30)
response = self.client.get(reverse("polls:index"))
self.assertQuerySetEqual(
response.context["latest_question_list"],
[question],
)
def test_two_past_questions(self):
"""
The questions index page may display multiple questions.
"""
question1 = create_question(question_text="Past question 1.", days=-30)
question2 = create_question(question_text="Past question 2.", days=-5)
response = self.client.get(reverse("polls:index"))
self.assertQuerySetEqual(
response.context["latest_question_list"],
[question2, question1],
)

+ 0
- 11
Django/mysite/polls/urls.py View File

@ -1,11 +0,0 @@
from django.urls import path
from . import views
app_name = "polls"
urlpatterns = [
path("", views.IndexView.as_view(), name="index"),
path("<int:pk>/", views.DetailView.as_view(), name="detail"),
path("<int:pk>/results/", views.ResultsView.as_view(), name="results"),
path("<int:question_id>/vote/", views.vote, name="vote"),
]

+ 0
- 62
Django/mysite/polls/views.py View File

@ -1,62 +0,0 @@
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from django.db.models import F
from django.views import generic
from django.utils import timezone
from .models import Choice, Question
# Create your views here.
class IndexView(generic.ListView):
template_name = "polls/index.html"
context_object_name = "latest_question_list"
def get_queryset(self):
"""
Return the last five published questions (not including those set to be
published in the future).
"""
return Question.objects.filter(pub_date__lte=timezone.now()).order_by("-pub_date")[
:5
]
class DetailView(generic.DetailView):
model = Question
template_name = "polls/detail.html"
def get_queryset(self):
"""
Excludes any questions that aren't published yet.
"""
return Question.objects.filter(pub_date__lte=timezone.now())
class ResultsView(generic.DetailView):
model = Question
template_name = "polls/results.html"
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST["choice"])
except (KeyError, Choice.DoesNotExist):
# Redisplay the question voting form.
return render(
request,
"polls/detail.html",
{
"question": question,
"error_message": "You didn't select a choice.",
},
)
else:
selected_choice.votes = F("votes") + 1
selected_choice.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))

+ 0
- 12
Django/mysite/templates/admin/base_site.html View File

@ -1,12 +0,0 @@
{% extends "admin/base.html" %}
{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
{% block branding %}
<div id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></div>
{% if user.is_anonymous %}
{% include "admin/color_theme_toggle.html" %}
{% endif %}
{% endblock %}
{% block nav-global %}{% endblock %}

+ 16
- 0
Libros/Pipfile View File

@ -0,0 +1,16 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
django = "*"
djangorestframework = "*"
epub-meta = "*"
django-db = "*"
pillow = "*"
[dev-packages]
[requires]
python_version = "3.10"

+ 172
- 0
Libros/Pipfile.lock View File

@ -0,0 +1,172 @@
{
"_meta": {
"hash": {
"sha256": "ef4cc3e12936cd221526bb3ceae7ac1b1b7593040426b99e3ef1b167fe85abf5"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.10"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"asgiref": {
"hashes": [
"sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47",
"sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"
],
"markers": "python_version >= '3.8'",
"version": "==3.8.1"
},
"django": {
"hashes": [
"sha256:bd4505cae0b9bd642313e8fb71810893df5dc2ffcacaa67a33af2d5cd61888f2",
"sha256:f216510ace3de5de01329463a315a629f33480e893a9024fc93d8c32c22913da"
],
"index": "pypi",
"markers": "python_version >= '3.10'",
"version": "==5.0.7"
},
"django-db": {
"hashes": [
"sha256:8639b6f78f04f34294cb465b308992a84297df41033519efd85b3a6b186b5296"
],
"index": "pypi",
"version": "==0.0.7"
},
"djangorestframework": {
"hashes": [
"sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20",
"sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==3.15.2"
},
"epub-meta": {
"hashes": [
"sha256:9d9c6afeaef796105a77d0aef33a1c943ccd91cccb2cb7716f59ad149819a4e9"
],
"index": "pypi",
"version": "==0.0.7"
},
"pillow": {
"hashes": [
"sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885",
"sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea",
"sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df",
"sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5",
"sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c",
"sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d",
"sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd",
"sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06",
"sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908",
"sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a",
"sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be",
"sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0",
"sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b",
"sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80",
"sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a",
"sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e",
"sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9",
"sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696",
"sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b",
"sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309",
"sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e",
"sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab",
"sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d",
"sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060",
"sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d",
"sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d",
"sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4",
"sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3",
"sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6",
"sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb",
"sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94",
"sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b",
"sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496",
"sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0",
"sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319",
"sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b",
"sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856",
"sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef",
"sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680",
"sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b",
"sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42",
"sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e",
"sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597",
"sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a",
"sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8",
"sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3",
"sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736",
"sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da",
"sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126",
"sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd",
"sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5",
"sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b",
"sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026",
"sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b",
"sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc",
"sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46",
"sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2",
"sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c",
"sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe",
"sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984",
"sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a",
"sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70",
"sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca",
"sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b",
"sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91",
"sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3",
"sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84",
"sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1",
"sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5",
"sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be",
"sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f",
"sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc",
"sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9",
"sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e",
"sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141",
"sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef",
"sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22",
"sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27",
"sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e",
"sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==10.4.0"
},
"pymysql": {
"hashes": [
"sha256:4de15da4c61dc132f4fb9ab763063e693d521a80fd0e87943b9a453dd4c19d6c",
"sha256:e127611aaf2b417403c60bf4dc570124aeb4a57f5f37b8e95ae399a42f904cd0"
],
"markers": "python_version >= '3.7'",
"version": "==1.1.1"
},
"sqlparse": {
"hashes": [
"sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4",
"sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e"
],
"markers": "python_version >= '3.8'",
"version": "==0.5.1"
},
"typing-extensions": {
"hashes": [
"sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d",
"sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"
],
"markers": "python_version < '3.11'",
"version": "==4.12.2"
}
},
"develop": {}
}

BIN
Libros/biblioteca/autores/auster.jpg View File

Before After
Width: 300  |  Height: 300  |  Size: 38 KiB

BIN
Libros/biblioteca/autores/daniel.jpg View File

Before After
Width: 959  |  Height: 1280  |  Size: 65 KiB

Django/mysite/mysite/__init__.py → Libros/biblioteca/biblioteca/__init__.py View File


Django/mysite/mysite/asgi.py → Libros/biblioteca/biblioteca/asgi.py View File

@ -1,5 +1,5 @@
""" """
ASGI config for mysite project.
ASGI config for biblioteca project.
It exposes the ASGI callable as a module-level variable named ``application``. It exposes the ASGI callable as a module-level variable named ``application``.
@ -11,6 +11,6 @@ import os
from django.core.asgi import get_asgi_application from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'biblioteca.settings')
application = get_asgi_application() application = get_asgi_application()

Django/mysite/mysite/settings.py → Libros/biblioteca/biblioteca/settings.py View File

@ -1,5 +1,5 @@
""" """
Django settings for mysite project.
Django settings for biblioteca project.
Generated by 'django-admin startproject' using Django 5.0.7. Generated by 'django-admin startproject' using Django 5.0.7.
@ -20,7 +20,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-wussf@h6r5!#*1ko$6k!h9i2%sv-a3y)m6v6$ehn4_a0t0b01&'
SECRET_KEY = 'django-insecure-q0bxrr=*ab*b8oa#=+-@_4cu-cp6)c3lz#+!c_%51$*!bnz%4!'
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
@ -31,13 +31,13 @@ ALLOWED_HOSTS = []
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
"polls.apps.PollsConfig",
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'gestion',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -50,12 +50,12 @@ MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
ROOT_URLCONF = 'mysite.urls'
ROOT_URLCONF = 'biblioteca.urls'
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / "templates"],
'DIRS': [],
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
@ -68,7 +68,7 @@ TEMPLATES = [
}, },
] ]
WSGI_APPLICATION = 'mysite.wsgi.application'
WSGI_APPLICATION = 'biblioteca.wsgi.application'
# Database # Database
@ -104,9 +104,9 @@ AUTH_PASSWORD_VALIDATORS = [
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/5.0/topics/i18n/ # https://docs.djangoproject.com/en/5.0/topics/i18n/
LANGUAGE_CODE = 'es-es'
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Europe/Madrid'
TIME_ZONE = 'UTC'
USE_I18N = True USE_I18N = True

Django/mysite/mysite/urls.py → Libros/biblioteca/biblioteca/urls.py View File

@ -1,5 +1,5 @@
""" """
URL configuration for mysite project.
URL configuration for biblioteca project.
The `urlpatterns` list routes URLs to views. For more information please see: The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.0/topics/http/urls/ https://docs.djangoproject.com/en/5.0/topics/http/urls/
@ -16,8 +16,14 @@ Including another URLconf
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [ urlpatterns = [
path("polls/", include("polls.urls")),
path('admin/', admin.site.urls),
path('obreros/', admin.site.urls),
path('gestion/', include('gestion.urls')),
] ]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Django/mysite/mysite/wsgi.py → Libros/biblioteca/biblioteca/wsgi.py View File

@ -1,5 +1,5 @@
""" """
WSGI config for mysite project.
WSGI config for biblioteca project.
It exposes the WSGI callable as a module-level variable named ``application``. It exposes the WSGI callable as a module-level variable named ``application``.
@ -11,6 +11,6 @@ import os
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'biblioteca.settings')
application = get_wsgi_application() application = get_wsgi_application()

BIN
Django/mysite/db.sqlite3 → Libros/biblioteca/db.sqlite3 View File


Django/mysite/polls/__init__.py → Libros/biblioteca/gestion/__init__.py View File


+ 9
- 0
Libros/biblioteca/gestion/admin.py View File

@ -0,0 +1,9 @@
from django.contrib import admin
# Register your models here.
from .models import Autor, Libro
admin.site.register(Autor)
admin.site.register(Libro)

Django/mysite/polls/apps.py → Libros/biblioteca/gestion/apps.py View File

@ -1,6 +1,6 @@
from django.apps import AppConfig from django.apps import AppConfig
class PollsConfig(AppConfig):
class GestionConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'polls'
name = 'gestion'

+ 12
- 0
Libros/biblioteca/gestion/forms.py View File

@ -0,0 +1,12 @@
from django import forms
from .models import Autor, Libro
class AutorForm(forms.ModelForm):
class Meta:
model = Autor
fields = ['nombre', 'biografia', 'foto']
class LibroForm(forms.ModelForm):
class Meta:
model = Libro
fields = ['titulo', 'autor', 'fecha_publicacion', 'descripcion', 'archivo', 'portada']

+ 34
- 0
Libros/biblioteca/gestion/migrations/0001_initial.py View File

@ -0,0 +1,34 @@
# Generated by Django 5.0.7 on 2024-08-05 09:29
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Autor',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nombre', models.CharField(max_length=200)),
('biografia', models.TextField(blank=True, null=True)),
],
),
migrations.CreateModel(
name='Libro',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('titulo', models.CharField(max_length=200)),
('fecha_publicacion', models.DateField()),
('descripcion', models.TextField(blank=True, null=True)),
('archivo', models.FileField(upload_to='libros/')),
('autor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='gestion.autor')),
],
),
]

+ 20
- 0
Libros/biblioteca/gestion/migrations/0002_alter_libro_fecha_publicacion.py View File

@ -0,0 +1,20 @@
# Generated by Django 5.0.7 on 2024-08-05 10:17
import django.core.validators
import gestion.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gestion', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='libro',
name='fecha_publicacion',
field=models.PositiveBigIntegerField(default=2024, validators=[django.core.validators.MinValueValidator(1984), gestion.models.max_value_current_year]),
),
]

+ 18
- 0
Libros/biblioteca/gestion/migrations/0003_libro_portada.py View File

@ -0,0 +1,18 @@
# Generated by Django 5.0.7 on 2024-08-05 10:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gestion', '0002_alter_libro_fecha_publicacion'),
]
operations = [
migrations.AddField(
model_name='libro',
name='portada',
field=models.ImageField(blank=True, null=True, upload_to='portadas/'),
),
]

+ 18
- 0
Libros/biblioteca/gestion/migrations/0004_autor_foto.py View File

@ -0,0 +1,18 @@
# Generated by Django 5.0.7 on 2024-08-05 10:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gestion', '0003_libro_portada'),
]
operations = [
migrations.AddField(
model_name='autor',
name='foto',
field=models.ImageField(blank=True, null=True, upload_to='autores/'),
),
]

Django/mysite/polls/migrations/__init__.py → Libros/biblioteca/gestion/migrations/__init__.py View File


+ 29
- 0
Libros/biblioteca/gestion/models.py View File

@ -0,0 +1,29 @@
from django.db import models
import datetime
from django.core.validators import MaxValueValidator, MinValueValidator
def current_year():
return datetime.date.today().year
def max_value_current_year(value):
return MaxValueValidator(current_year())(value)
class Autor(models.Model):
nombre = models.CharField(max_length=200)
biografia = models.TextField(blank=True, null=True)
foto = models.ImageField(upload_to='autores/', blank=True, null=True) # Nuevo campo
def __str__(self):
return self.nombre
class Libro(models.Model):
titulo = models.CharField(max_length=200)
autor = models.ForeignKey(Autor, on_delete=models.CASCADE)
fecha_publicacion = models.PositiveBigIntegerField(default=current_year(), validators=[MinValueValidator(1984), max_value_current_year])
descripcion = models.TextField(blank=True, null=True)
archivo = models.FileField(upload_to='libros/')
portada = models.ImageField(upload_to='portadas/', blank=True, null=True) # Nuevo campo
def __str__(self):
return self.titulo

+ 14
- 0
Libros/biblioteca/gestion/templates/gestion/detalle_autor.html View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<title>Detalle del Autor</title>
</head>
<body>
<h1>{{ autor.nombre }}</h1>
<p>{{ autor.biografia }}</p>
{% if autor.foto %}
<img src="{{ autor.foto.url }}" alt="Foto del autor" width="200">
{% endif %}
<a href="{% url 'lista_autores' %}">Volver a la lista de autores</a>
</body>
</html>

+ 18
- 0
Libros/biblioteca/gestion/templates/gestion/detalle_libro.html View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>Detalle del Libro</title>
</head>
<body>
<h1>{{ libro.titulo }}</h1>
<p>{{ libro.descripcion }}</p>
<p><strong>Autor:</strong> {{ libro.autor.nombre }}</p>
<p><strong>Fecha de Publicación:</strong> {{ libro.fecha_publicacion }}</p>
{% if libro.portada %}
<img src="{{ libro.portada.url }}" alt="Portada del libro" width="200">
{% endif %}
{% if libro.archivo %}
<p><a href="{{ libro.archivo.url }}">Descargar</a></p>
{% endif %} <a href="{% url 'lista_libros' %}">Volver a la lista de libros</a>
</body>
</html>

+ 15
- 0
Libros/biblioteca/gestion/templates/gestion/form_autor.html View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>Formulario de Autor</title>
</head>
<body>
<h1>{% if form.instance.pk %}Editar Autor{% else %}Nuevo Autor{% endif %}</h1>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Guardar</button>
</form>
<a href="{% url 'lista_autores' %}">Cancelar</a>
</body>
</html>

+ 15
- 0
Libros/biblioteca/gestion/templates/gestion/form_libro.html View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>Formulario de Libro</title>
</head>
<body>
<h1>{% if form.instance.pk %}Editar Libro{% else %}Nuevo Libro{% endif %}</h1>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Guardar</button>
</form>
<a href="{% url 'lista_libros' %}">Cancelar</a>
</body>
</html>

+ 19
- 0
Libros/biblioteca/gestion/templates/gestion/lista_autores.html View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>Lista de Autores</title>
</head>
<body>
<h1>Lista de Autores</h1>
<a href="{% url 'nuevo_autor' %}">Nuevo Autor</a>
<ul>
{% for autor in autores %}
<li>
<a href="{% url 'detalle_autor' autor.id %}">{{ autor.nombre }}</a>
<a href="{% url 'editar_autor' autor.id %}">Editar</a>
<a href="{% url 'eliminar_autor' autor.id %}">Eliminar</a>
</li>
{% endfor %}
</ul>
</body>
</html>

+ 19
- 0
Libros/biblioteca/gestion/templates/gestion/lista_libros.html View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>Lista de Libros</title>
</head>
<body>
<h1>Lista de Libros</h1>
<a href="{% url 'nuevo_libro' %}">Nuevo Libro</a>
<ul>
{% for libro in libros %}
<li>
<a href="{% url 'detalle_libro' libro.id %}">{{ libro.titulo }}</a>
<a href="{% url 'editar_libro' libro.id %}">Editar</a>
<a href="{% url 'eliminar_libro' libro.id %}">Eliminar</a>
</li>
{% endfor %}
</ul>
</body>
</html>

+ 3
- 0
Libros/biblioteca/gestion/tests.py View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

+ 16
- 0
Libros/biblioteca/gestion/urls.py View File

@ -0,0 +1,16 @@
from django.urls import path
from . import views
urlpatterns = [
path('autores/', views.lista_autores, name='lista_autores'),
path('autores/nuevo/', views.nuevo_autor, name='nuevo_autor'),
path('autores/<int:autor_id>/', views.detalle_autor, name='detalle_autor'),
path('autores/<int:autor_id>/editar/', views.editar_autor, name='editar_autor'),
path('autores/<int:autor_id>/eliminar/', views.eliminar_autor, name='eliminar_autor'),
path('libros/', views.lista_libros, name='lista_libros'),
path('libros/nuevo/', views.nuevo_libro, name='nuevo_libro'),
path('libros/<int:libro_id>/', views.detalle_libro, name='detalle_libro'),
path('libros/<int:libro_id>/editar/', views.editar_libro, name='editar_libro'),
path('libros/<int:libro_id>/eliminar/', views.eliminar_libro, name='eliminar_libro'),
]

+ 73
- 0
Libros/biblioteca/gestion/views.py View File

@ -0,0 +1,73 @@
from django.shortcuts import render, get_object_or_404, redirect
from .models import Autor, Libro
from .forms import AutorForm, LibroForm
# Vistas para los autores
def lista_autores(request):
autores = Autor.objects.all()
return render(request, 'gestion/lista_autores.html', {'autores': autores})
def detalle_autor(request, autor_id):
autor = get_object_or_404(Autor, pk=autor_id)
return render(request, 'gestion/detalle_autor.html', {'autor': autor})
def nuevo_autor(request):
if request.method == 'POST':
form = AutorForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('lista_autores')
else:
form = AutorForm()
return render(request, 'gestion/form_autor.html', {'form': form})
def editar_autor(request, autor_id):
autor = get_object_or_404(Autor, pk=autor_id)
if request.method == 'POST':
form = AutorForm(request.POST, request.FILES, instance=autor)
if form.is_valid():
form.save()
return redirect('lista_autores')
else:
form = AutorForm(instance=autor)
return render(request, 'gestion/form_autor.html', {'form': form})
def eliminar_autor(request, autor_id):
autor = get_object_or_404(Autor, pk=autor_id)
autor.delete()
return redirect('lista_autores')
# Vistas para los libros
def lista_libros(request):
libros = Libro.objects.all()
return render(request, 'gestion/lista_libros.html', {'libros': libros})
def detalle_libro(request, libro_id):
libro = get_object_or_404(Libro, pk=libro_id)
return render(request, 'gestion/detalle_libro.html', {'libro': libro})
def nuevo_libro(request):
if request.method == 'POST':
form = LibroForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('lista_libros')
else:
form = LibroForm()
return render(request, 'gestion/form_libro.html', {'form': form})
def editar_libro(request, libro_id):
libro = get_object_or_404(Libro, pk=libro_id)
if request.method == 'POST':
form = LibroForm(request.POST, request.FILES, instance=libro)
if form.is_valid():
form.save()
return redirect('lista_libros')
else:
form = LibroForm(instance=libro)
return render(request, 'gestion/form_libro.html', {'form': form})
def eliminar_libro(request, libro_id):
libro = get_object_or_404(Libro, pk=libro_id)
libro.delete()
return redirect('lista_libros')

BIN
Libros/biblioteca/libros/Invisible_-_Paul_Auster_5.epub View File


BIN
Libros/biblioteca/libros/Sunset_Park_-_Paul_Auster_12.epub View File


Django/mysite/manage.py → Libros/biblioteca/manage.py View File

@ -6,7 +6,7 @@ import sys
def main(): def main():
"""Run administrative tasks.""" """Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'biblioteca.settings')
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:

BIN
Libros/biblioteca/portadas/sunsetpark.jpeg View File

Before After
Width: 533  |  Height: 800  |  Size: 242 KiB

Loading…
Cancel
Save