Django<加载static目录下的CSS-JS>

本文详细介绍了解决Django项目中加载静态文件如JS和CSS的问题。通过设置STATIC_URL、STATIC_ROOT及STATICFILES_DIRS,并使用collectstatic命令收集静态文件,最终实现在HTML中正确引用这些资源。

博主之前没有接触过Django,最近有一个项目需求要做一个可视化的展示,但是一直无法加载static文件下的JS和CSS,经过一番折腾,最后终于搞定了。下面是完整的过程:

1.我的项目结构:

这里写图片描述

2.在与settings.py文件的统计目录下,新建static文件夹,然后在新建两个子目录(static_dirs、static_root),然后在settings.py文件中添加以下代码:

STATIC_URL = '/static/'

STATIC_ROOT = os.path.join(BASE_DIR, 'mysite', 'static', 'static_root')

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'mysite', 'static', 'static_dirs'),
)

3.在pycharm的终端中运行shell

 python manage.py collectstatic

这里写图片描述
不出意外的话,static/static_root下会多出这么写文件

这里写图片描述

接下来将需要使用到的js和css文件放到对应的目录下,如下图:

这里写图片描述

在html中引入这些js、css

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    {% load static %}
    <link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap.css' %}">
    <script src="{% static 'js/jquery-3.2.1.min.js' %}"></script>
    <script src="{% static 'js/bootstrap.js' %}"></script>
</head>
<body>
<!-- 标准的按钮 -->
<button type="button" class="btn btn-default">默认按钮</button>
<!-- 提供额外的视觉效果,标识一组按钮中的原始动作 -->
<button type="button" class="btn btn-primary">原始按钮</button>
<!-- 表示一个成功的或积极的动作 -->
<button type="button" class="btn btn-success">成功按钮</button>
<!-- 信息警告消息的上下文按钮 -->
<button type="button" class="btn btn-info">信息按钮</button>
<!-- 表示应谨慎采取的动作 -->
<button type="button" class="btn btn-warning">警告按钮</button>
<!-- 表示一个危险的或潜在的负面动作 -->
<button type="button" class="btn btn-danger">危险按钮</button>
<!-- 并不强调是一个按钮,看起来像一个链接,但同时保持按钮的行为 -->
<button type="button" class="btn btn-link">链接按钮</button>
<table class="table">
    <caption>基本的表格布局</caption>
   <thead>
      <tr>
         <th>名称</th>
         <th>城市</th>
      </tr>
   </thead>
   <tbody>
      <tr>
         <td>Tanmay</td>
         <td>Bangalore</td>
      </tr>
      <tr>
         <td>Sachin</td>
         <td>Mumbai</td>
      </tr>
   </tbody>
</table>
</body>
</html>

然后重启App, 观察浏览器的效果,可以看到效果已经加载进来了:
这里写图片描述

至此,相应的文件已经加载进来了。待我有时间好好学一学Django之后,再来解释其中的奥秘。

index.html: <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>闽货优品 - 福建特产文化推广</title> <link rel="stylesheet" href="style.css"> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> </head> <body> <div id="app"> <!-- 顶部导航栏 --> <nav class="main-nav"> <div class="container"> <a href="index.html" class="nav-logo">🍜 闽货优品</a> <div class="nav-links"> <a href="index.html" class="nav-link active">首页</a> <a href="products.html" class="nav-link">特产档案</a> <a href="craftsmen.html" class="nav-link">匠人故事</a> <a href="videos.html" class="nav-link">短视频</a> <a href="cold-knowledge.html" class="nav-link">冷知识</a> </div> </div> </nav> <!-- 文化标语 --> <section class="hero-banner"> <div class="container"> <h1 class="hero-title">味道,是故乡最深的记忆</h1> <p class="hero-subtitle">探寻福建非遗美食,传承千年文化韵味</p> </div> </section> <!-- 文化冷知识 --> <!-- 在 index.html 的文化冷知识部分 --> <section class="cultural-trivia-section"> <div class="container"> <div class="section-header"> <span class="section-icon">🎯</span> <h2 class="section-title">每日文化冷知识</h2> <p class="section-subtitle">每天发现闽清非遗的新鲜故事</p> </div> <!-- 冷知识轮播 --> <!-- 冷知识轮播 --> <div class="trivia-carousel"> <div class="carousel-container"> <div v-for="(trivia, index) in dailyTrivias" :key="index" :class="['carousel-item', { active: currentTriviaIndex === index }]"> <div class="trivia-content"> <!-- 图标和分类同行 --> <div class="trivia-header"> <span class="trivia-icon">{{ trivia.icon }}</span> <h3 class="trivia-category">{{ trivia.category }}</h3> </div> <p>{{ trivia.content }}</p> </div> </div> </div> <!-- 轮播指示器 --> <div class="carousel-indicators" v-if="dailyTrivias.length > 1"> <button v-for="(_, index) in dailyTrivias" :key="index" :class="['indicator', { active: currentTriviaIndex === index }]" @click="currentTriviaIndex = index"> </button> </div> <!-- 轮播控制按钮 --> <button class="carousel-btn prev" @click="prevTrivia" v-if="dailyTrivias.length > 1">‹</button> <button class="carousel-btn next" @click="nextTrivia" v-if="dailyTrivias.length > 1">›</button> </div> <!-- 特产档案 --> <section class="products-section"> <div class="container"> <h2 class="section-title">🍜 特色美食档案</h2> <!-- 加载状态 --> <div v-if="isLoading" class="loading"> <p>正在加载特产数据...</p> </div> <!-- 产品网格 --> <div v-else class="products-grid"> <div v-for="product in products" :key="product.id" class="product-card"> <!-- 添加产品主图 --> <img v-if="mainImageUrl" :src="'http://127.0.0.1:8000' + product.main_image_url" :alt="product.product_name" class="hero-image"> <div class="product-header"> <h3>{{ product.product_name }}</h3> <span class="category">{{ product.category }}</span> </div> <div class="product-origin">📍 {{ product.origin }}</div> <div class="product-taste">👅 {{ product.taste_profile }}</div> <button class="detail-btn" @click="showDetail(product)">查看详情</button> </div> </div> <!-- 空状态 --> <div v-if="!isLoading && products.length === 0" class="no-data"> <p>暂无特产数据</p> </div> <!-- 查看更多 --> <div class="more-section"> <a href="products.html" class="btn-primary">浏览全部特产档案</a> </div> </div> </section> <footer class="footer"> <div class="container"> <p>闽货优品 - 传承福建美食文化 © 2024</p> </div> </footer> </div> <script src="app.js"></script> </body> </html> 匠人界面: <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>匠人故事 - 闽货优品</title> <link rel="stylesheet" href="style.css"> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> </head> <body> <div id="app"> <!-- 顶部导航栏 --> <nav class="main-nav"> <div class="container"> <a href="index.html" class="nav-logo">🍜 闽货优品</a> <div class="nav-links"> <a href="index.html" class="nav-link">首页</a> <a href="products.html" class="nav-link">特产档案</a> <a href="craftsmen.html" class="nav-link active">匠人故事</a> <a href="videos.html" class="nav-link">短视频</a> <a href="cold-knowledge.html" class="nav-link">冷知识</a> </div> </div> </nav> <!-- 匠人故事标语 --> <section class="hero-banner"> <div class="container"> <h1 class="hero-title">匠心传承,非遗之光</h1> <p class="hero-subtitle">聆听非遗传承人的坚守与创新故事</p> </div> </section> <!-- 匠人故事列表 --> <section class="craftsmen-section"> <div class="container"> <h2 class="section-title">👨‍🍳 非遗传承匠人</h2> <!-- 加载状态 --> <div v-if="isLoading" class="loading"> <p>正在加载匠人故事...</p> </div> <!-- 匠人网格 --> <div v-else class="craftsmen-grid"> <div v-for="craftsman in craftsmen" :key="craftsman.id" class="craftsman-card"> <!-- 匠人头像 --> <div class="craftsman-avatar"> <div class="avatar-placeholder"> 👨‍🍳 </div> </div> <div class="craftsman-content"> <h3 class="craftsman-name">{{ craftsman.name }}</h3> <div class="craftsman-specialty">🏷️ {{ craftsman.specialty_name }}</div> <div class="craftsman-experience">⏳ {{ craftsman.years_experience }}年经验</div> <p class="craftsman-story">{{ getStoryExcerpt(craftsman.story_content) }}</p> <div class="craftsman-honors" v-if="craftsman.honors"> <strong>🏆 荣誉:</strong> {{ craftsman.honors }} </div> <button class="detail-btn" @click="showCraftsmanDetail(craftsman)">了解更多</button> </div> </div> </div> <!-- 空状态 --> <div v-if="!isLoading && craftsmen.length === 0" class="no-data"> <p>暂无匠人数据</p> </div> </div> </section> <footer class="footer"> <div class="container"> <p>闽货优品 - 传承福建美食文化 © 2024</p> </div> </footer> </div> <script> const { createApp } = Vue; createApp({ data() { return { craftsmen: [], isLoading: true, API_BASE: 'http://127.0.0.1:8000' } }, async mounted() { await this.loadCraftsmen(); }, methods: { async loadCraftsmen() { try { console.log('正在加载匠人数据...'); const response = await fetch(`${this.API_BASE}/api/craftsmen/`); if (!response.ok) { throw new Error('匠人API请求失败'); } let data = await response.json(); this.craftsmen = data; console.log('匠人数据加载成功:', this.craftsmen); } catch (error) { console.error('加载匠人失败:', error); this.craftsmen = []; } finally { this.isLoading = false; } }, getStoryExcerpt(story) { if (!story) return '暂无故事内容'; return story.length > 100 ? story.substring(0, 100) + '...' : story; }, showCraftsmanDetail(craftsman) { alert(`${craftsman.name}\n\n${craftsman.story_content}\n\n荣誉: ${craftsman.honors || '暂无'}`); } } }).mount('#app'); </script> </body> </html> app.js: const { createApp } = Vue; createApp({ data() { return { products: [], isLoading: true, // 冷知识轮播数据 dailyTrivias: [], currentTriviaIndex: 0, autoPlayInterval: null, API_BASE: 'http://127.0.0.1:8000' } }, async mounted() { await this.loadProducts(); await this.loadDailyTrivias(); this.startAutoPlay(); }, methods: { async loadProducts() { try { const response = await fetch('http://127.0.0.1:8000/api/specialties/'); this.products = await response.json(); // 强制修改图片URL为绝对路径 this.products = this.products.map(product => { if (product.main_image_url) { product.main_image_url = 'http://127.0.0.1:8000' + product.main_image_url; } if (product.recipes) { product.recipes = product.recipes.map(recipe => { if (recipe.image_url) { recipe.image_url = 'http://127.0.0.1:8000' + recipe.image_url; } return recipe; }); } return product; }); } catch (error) { console.error('加载特产失败:', error); this.products = []; } finally { this.isLoading = false; } }, async loadDailyTrivias() { try { console.log('正在加载每日冷知识...'); const response = await fetch('http://127.0.0.1:8000/api/daily-trivias/'); if (response.ok) { this.dailyTrivias = await response.json(); console.log('冷知识加载成功:', this.dailyTrivias); } else { console.warn('冷知识API请求失败,使用默认数据'); this.dailyTrivias = this.getDefaultTrivias(); } } catch (error) { console.error('加载冷知识失败:', error); this.dailyTrivias = this.getDefaultTrivias(); } }, getDefaultTrivias() { // 默认冷知识数据 return [ { icon: "🧂", category: "闽清糟菜", content: "非遗技艺:闽清糟菜采用传统陶缸发酵工艺,2017年列入福州非遗" }, { icon: "🤖", category: "创新故事", content: "机器人主厨:全国首家机器人制作闽清美食的数字化餐厅" }, { icon: "🌿", category: "闽清糟菜", content: "健康价值:富含乳酸菌和氨基酸,是天然的益生元食品" } ]; }, nextTrivia() { this.currentTriviaIndex = (this.currentTriviaIndex + 1) % this.dailyTrivias.length; this.resetAutoPlay(); }, prevTrivia() { this.currentTriviaIndex = (this.currentTriviaIndex - 1 + this.dailyTrivias.length) % this.dailyTrivias.length; this.resetAutoPlay(); }, startAutoPlay() { if (this.dailyTrivias.length > 1) { this.autoPlayInterval = setInterval(() => { this.nextTrivia(); }, 5000); // 5秒自动切换 } }, resetAutoPlay() { if (this.autoPlayInterval) { clearInterval(this.autoPlayInterval); this.startAutoPlay(); } }, showDetail(product) { window.location.href = `detail.html?id=${product.id}`; }, closeModal() { this.selectedProduct = null; } }, beforeUnmount() { // 清理定时器 if (this.autoPlayInterval) { clearInterval(this.autoPlayInterval); } } }).mount('#app');以上是我的代码,问题是我的首页本应该是特产档案的地方,出现了匠人档案
最新发布
11-09
{% load bootstrap4 %} <!doctype html> <html lang="en"> <head> <meta charest="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1, shrink-to-fit=no"> <title>Learning Log</title> {% bootstrap_css %} {% bootstrap_javascript jquery='full' %} </head> <body> <nav class="navbar navbar-expand-md navbar-light bg-light mb-4 border"> <a class="navbar-band" href="{% url 'learning_logs:index' %}">Learning Log</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Taggle navigation"> <span class="navbar-toggler-icon"></span></button> <div class="collapse navbar-collaspe" id="navbarCollapse"> <ul class="navbar-nav mr-auto"> <li class="nav-item"> <a class="nav-link" href="{% url 'learning_logs:topics' %}">Topics</a> </li> </ul> <ul class="navbar-nav ml-auto"> {% if user.is_authenticated %} <li class="nav-item"> <span class="navbar-text">Hello,{{ user.username }}.</span> </li> <li class="nav-item"> <form method='post' action="{% url 'users:logout' %}" class="d-inline"> {% csrf_token %} <button type="submit" class="bth bth-link p-0 border-0">Log out</button> </form> </li> {% else %} <li class="nav-item"> <a class="nav-link" href="{% url 'users:register' %}">Register</a> </li> <li class="nav-item"> <a class="nav-link" href="{url 'users:login' %}">Log in</a> </li> {% endif %} </ul> </div> </nav> <main role="main" class="container"> <div class="pb-2 mb-2 border-bottom"> {% block page_header %}{% endblock page_header %} </div> <div> {% block content %}{% endblock content %} </div> </main> </body> </html>
09-14
<!DOCTYPE html> {% load static %} <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="icon" href="{% static 'publicStatic/images/favicon.ico' %}"> <link rel="stylesheet" href="{% static "css/font-awesome.min.css" %}"> {#<link href="{% static "bootstrap/css/bootstrap.css" %}" rel="stylesheet" type="text/css">#} <!-- bootstrap-theme.css 需要屏蔽掉,否则和bootstrap.css冲突,造成 下拉菜单悬浮 的子菜单项背景色不起作用 --> <link href="{% static "bootstrap/css/bootstrap-theme.css" %}" rel="stylesheet" type="text/css"> {# <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">#} <link rel="stylesheet" href="{% static "bootstrap/css/bootstrap.min.css" %}" type="text/css" > <script src="{% static "bootstrap/jquery-1.9.1.js" %}"></script> <script src="{% static "bootstrap/js/bootstrap.js" %}"></script> <!--20250928添加bootstrap.bundle.min.js,否则当鼠标悬停在‘项目管理’下面的二级菜单,不会显示二级菜单下的三件菜单 另外,这里添加之后,打开网页必须进行 ctr+F5 刷新一次页面才可以,否则无效果--> <script src="{% static "sweetalert/sweetalert.min.js" %}" ></script> <link rel="stylesheet" href="{% static "sweetalert/sweetalert.min.css" %}" > <script src="{% static "bootstrap/js/bootstrap.bundle.min.js" %}"></script> <script type="text/javascript" src="{%static 'datepicker/js/bootstrap-datepicker.js' %}" ></script> <link rel="stylesheet" type="text/css" href="{% static 'datepicker/css/datepicker.css' %}"> </head> <body> <!-- 工单表格 --> <table class="table table-hover" name="processedorder" id="processedorder"> <thead> <tr> <th>序号</th> <th>工作令号</th> <th>名称</th> <th>图号</th> <th>数量</th> <th>要求完成时间</th> <th>图纸数量</th> <th>整件号</th> </tr> </thead> <tbody> <!-- 动态生成的行将在这里添加 --> </tbody> </table> <!-- 设备表格 --> <P class="text-center"><b>使用设备</b></P> <table class="table table-bordered table-hover text-center" name="device" id="device"> <thead> <tr> <th>设备ID</th> <th>设备名称</th> <th>序列号</th> <th>开始时间</th> <th>截止时间</th> <th>操作</th> </tr> </thead> <tbody> <!-- 设备行将在这里添加 --> </tbody> </table> <!-- 控制按钮 --> <button id="button_processedorder">添加工单行</button> <button id="button_device">选择设备</button> <button id="button_save">保存工单</button> <!-- 设备选择模态框 --> <div id="device_modal" class="modal fade" style="display: none;"> <!-- 模态框内容 --> <div class="modal-dialog"> <div class="modal-content"> <!-- 模态框头部 --> <div class="modal-header"> <h4 class="modal-title">仪器一览表</h4> <button type="button" class="close" data-dismiss="modal">×</button> </div> <!-- 模态框主体 --> <div class="modal-body"> <table id="device_table"> <!-- 设备数据将在这里动态加载 --> </table> </div> <!-- 模态框底部 --> <div class="modal-footer"> <button type="button" class="btn btn-secondary close" data-dismiss="modal">关闭</button> </div> </div> </div> </div> <script> $(document).ready(function() { function safeInitDatepicker(element) { const dateStr = element.text().trim(); if (!dateStr) { element.text(new Date().toISOString().split('T')[0]); } else if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) { element.text(new Date(dateStr).toISOString().split('T')[0]); } element.datepicker({ format: 'yyyy-mm-dd', autoclose: true, startDate: new Date() }); } // 假设模态框的 ID 是 deviceModal $('table#processedorder td').attr('contenteditable', true); $('table#device td').attr('contenteditable', true); <!--解决模态点击关闭,无法关闭的问题--> $('.close').click(function() { $('#device_modal').modal('hide'); }); // 选择设备按钮 $('#button_device').click(function() { $.get('/project/get_devices/', function(response) { let html = ''; {#alert('11111111111111');#} if (response && response.devices) { // 添加空值检查 $.each(response.devices, function(i, device) { html += `<tr> <td>${device.id}</td> <td>${device.name}</td> <td>${device.serial}</td> <td><button class="select-device" data-id="${device.id}">选择</button></td> </tr>`; }); {#alert(html)#} $('#device_table').html(html); $('#device_modal').modal('show'); {#alert('33333333333');#} } else { console.error('设备数据格式错误:', response); } }); // 设备选择事件 $(document).on('click', '.select-device', function(event) { event.preventDefault(); let selectedDevice = $(this).data('id').toString(); {#alert(selectedDevice)#} let exists = false; $('#device tbody tr').each(function () { if ($(this).find('td:first').text() === selectedDevice) { exists = true; alert('有重复,请选择其它') return false; // 停止遍历 } }); {#alert(exists)#} const deviceId = $(this).data('id'); $.get(`/project/get_device_details/${deviceId}/`, function(device) { if (!exists) { $('#device tbody').append(` <tr data-id="${device.id}"> <td>${device.id}</td> <td>${device.name}</td> <td>${device.serial}</td> <td class="datepicker" >${device.current_date}</td> <td class="datepicker" >${device.current_date}</td> <td><button class="delete-item" >删除</button></td> </tr> `); } $(this).closest('tr').remove(); <!--必须要有下面这条指令,否则不能编辑--> // 设置倒数第一列和倒数第二列可编辑 // 为新添加的行设置可编辑和绑定 datepicker $('#device tbody tr:last').find('td').eq(-2).attr('contenteditable', true); // 倒数第一列 $('#device tbody tr:last').find('td').eq(-3).attr('contenteditable', true); // 倒数第二列 // 绑定删除事件(使用事件委托) $('#device tbody tr').on('click', '.delete-item', function() { $(this).closest('tr').remove();}); {#const newRow = $('#device tbody tr:last');#} {#newRow.find('.datepicker').each(function() {#} {# initDatepicker($(this));});#} {#$('#device tbody tr:last').find('td.datepicker').datepicker({#} {# format: 'yyyy-mm-dd', // 确保年份显示#} {# autoclose: true,#} {# defaultDate: new Date()});#} }); }); }); }); </script> </body> </html> 当关闭<div id="device_modal"之后,再次点击‘选择设备’按钮,则在<div id="device_modal" 点击‘选择’一条记录时候,会在前面的<table class="table table-bordered table-hover text-center" name="device" id="device"> 生产2条记录,当再次关闭 model,再次打开,每次回生产3次记录
09-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值