add device details

This commit is contained in:
Cayo Puigdefabregas 2024-07-05 15:32:07 +02:00
parent 07d0aa6f1c
commit df63af3ee0
16 changed files with 1123 additions and 344 deletions

55
dashboard/mixins.py Normal file
View File

@ -0,0 +1,55 @@
from django.urls import resolve
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import PermissionDenied
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.base import TemplateView
class Http403(PermissionDenied):
status_code = 403
default_detail = _('Permission denied. User is not authenticated')
default_code = 'forbidden'
def __init__(self, details=None, code=None):
if details is not None:
self.detail = details or self.default_details
if code is not None:
self.code = code or self.default_code
class DashboardView(LoginRequiredMixin):
login_url = "/login/"
template_name = "dashboard.html"
breadcrumb = ""
title = ""
subtitle = ""
section = ""
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'title': self.title,
'subtitle': self.subtitle,
'breadcrumb': self.breadcrumb,
# 'icon': self.icon,
'section': self.section,
'path': resolve(self.request.path).url_name,
'user': self.request.user,
})
return context
class DetailsMixin(DashboardView, TemplateView):
def get(self, request, *args, **kwargs):
self.pk = kwargs['pk']
self.object = get_object_or_404(self.model, pk=self.pk)
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'object': self.object,
})
return context

View File

@ -1,72 +1,205 @@
<!DOCTYPE html>
{% load i18n static %}
<!doctype html>
<html lang="en">
<head>
{% block head %}
{% block meta %}
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="robots" content="NONE,NOARCHIVE" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="Pangea">
{% endblock %}
<title>{% block title %}{% if title %}{{ title }} {% endif %}DeviceHub{% endblock %}</title>
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<!-- Bootstrap core CSS -->
{% block style %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href= "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<link href="{% static "/css/bootstrap.min.css" %}" rel="stylesheet">
<title>{% block page_title %}{% endblock %} - Usody</title>
<meta content="" name="description">
<meta content="" name="keywords">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
<!-- Favicons -->
<link href="{{ url_for('static', filename='img/favicon.png') }}" rel="icon">
<link href="{{ url_for('static', filename='img/apple-touch-icon.png') }}" rel="apple-touch-icon">
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
html, body {
height: 100%;
}
<!-- Google Fonts -->
<link href="https://fonts.gstatic.com" rel="preconnect">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i|Nunito:300,300i,400,400i,600,600i,700,700i|Poppins:300,300i,400,400i,500,500i,600,600i,700,700i" rel="stylesheet">
body {
display: flex;
flex-direction: column;
}
<!-- JS Files -->
<script src="{{ url_for('static', filename='js/jquery-3.6.0.min.js') }}"></script>
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@5.0.3" type="text/javascript"></script>
.main-content {
flex-grow: 1;
}
footer {
width: 100%;
}
</style>
<!-- Custom styles for this template -->
<link href="{% static "/css/dashboard.css" %}" rel="stylesheet">
{% endblock %}
{% endblock %}
</head>
<body>
<header class="navbar navbar-dark sticky-top admin bg-green flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="#">DEVICE HUB</a>
<div class="navbar-nav navbar-sub-brand">
PANGEA
</div>
<div class="navbar-nav">
<div class="nav-item text-nowrap">
<i id="user-avatar" class="bi bi-person-circle"></i>
<a class="navbar-sub-brand px-3" href="#">{{ user.email }}</a>
<a class="logout" href="{% url 'login:logout' %}">
<i class="fa-solid fa-arrow-right-from-bracket"></i>
</a>
</div>
</div>
</header>
<!-- Vendor CSS Files -->
<link href="{{ url_for('static', filename='vendor/bootstrap/css/bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='vendor/bootstrap-icons/bootstrap-icons.css') }}" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/simple-datatables@5.0.3/dist/style.css" rel="stylesheet" type="text/css">
<div class="container-fluid">
<div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="position-sticky pt-5">
<ul class="nav flex-column">
<li class="nav-item">
<a class="admin {% if section == 'Devices' %}active {% endif %}nav-link fw-bold" data-bs-toggle="collapse" data-bs-target="#ul_devices" aria-expanded="false" aria-controls="ul_devices" href="javascript:void()">
<i class="bi bi-laptop icon_sidebar"></i>
{% trans 'Devices' %}
</a>
<ul class="flex-column mb-2 ul_sidebar accordion-collapse {% if section == 'People' %}expanded{% else %}collapse{% endif %}" id="ul_devices" data-bs-parent="#sidebarMenu">
<li class="nav-item">
<a class="nav-link{% if path == 'admin_people_list' %} active2{% endif %}" href="{% url 'dashboard:unassigned_devices' %}">
{% trans 'Unassigned devices' %}
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == 'admin_people_new' %} active2{% endif %}" href="{# url 'idhub:admin_people_new' #}">
{% trans 'All devices' %}
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a class="admin {% if section == 'People' %}active {% endif %}nav-link fw-bold" data-bs-toggle="collapse" data-bs-target="#ul_lots" aria-expanded="false" aria-controls="ul_lots" href="javascript:void()">
<i class="bi bi-database icon_sidebar"></i>
{% trans 'Lots' %}
</a>
<ul class="flex-column mb-2 ul_sidebar accordion-collapse {% if section == 'People' %}expanded{% else %}collapse{% endif %}" id="ul_lots" data-bs-parent="#sidebarMenu">
<li class="nav-items">
<a class="nav-link{% if path == 'admin_people_list' %} active2{% endif %}" href="{# url 'idhub:admin_people_list' #}">
{% trans 'Incoming ' %}
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == 'admin_people_new' %} active2{% endif %}" href="{# url 'idhub:admin_people_new' #}">
{% trans 'Outgoing ' %}
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == 'admin_people_new' %} active2{% endif %}" href="{# url 'idhub:admin_people_new' #}">
{% trans 'Temporal ' %}
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a class="admin {% if section == 'People' %}active {% endif %}nav-link fw-bold" data-bs-toggle="collapse" data-bs-target="#ul_snapshots" aria-expanded="false" aria-controls="ul_snapshots" href="javascript:void()">
<i class="bi bi-usb-drive icon_sidebar"></i>
{% trans 'Snapshots' %}
</a>
<ul class="flex-column mb-2 ul_sidebar accordion-collapse {% if section == 'People' %}expanded{% else %}collapse{% endif %}" id="ul_snapshots" data-bs-parent="#sidebarMenu">
<li class="nav-item">
<a class="nav-link{% if path == 'admin_people_list' %} active2{% endif %}" href="{# url 'idhub:admin_people_list' #}">
{% trans 'Upload one' %}
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == 'admin_people_list' %} active2{% endif %}" href="{# url 'idhub:admin_people_list' #}">
{% trans 'Old snapshots' %}
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a class="admin {% if section == 'People' %}active {% endif %}nav-link fw-bold" data-bs-toggle="collapse" data-bs-target="#ul_placeholders" aria-expanded="false" aria-controls="ul_placeholders" href="javascript:void()">
<i class="bi-menu-button-wide icon_sidebar"></i>
{% trans 'Placeholders' %}
</a>
<ul class="flex-column mb-2 ul_sidebar accordion-collapse {% if section == 'People' %}expanded{% else %}collapse{% endif %}" id="ul_placeholders" data-bs-parent="#sidebarMenu">
<li class="nav-item">
<a class="nav-link{% if path == 'admin_people_list' %} active2{% endif %}" href="{# url 'idhub:admin_people_list' #}">
{% trans 'Upload Spreadsheet' %}
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == 'admin_people_list' %} active2{% endif %}" href="{% url 'device:add' %}">
{% trans 'Create one' %}
</a>
</li>
</ul>
</li>
</ul>
</div>
</nav>
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
{% block messages %}
{% for message in messages %}
<div class="alert {{ message.tags|default:'info' }} alert-dismissible fade show mt-3" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
</button>
</div>
{% endfor %}
{% endblock messages %}
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2">
<h1 class="h2">{{ title }}</h1>
</div>
<div class="row border-bottom mb-3">
<div class="col">
<small style="color:#899bbd"><i>{{ breadcrumb }}</i></small>
</div>
<div class="btn-toolbar mb-2 mb-md-0">
</div>
</div>
<!-- Template Main CSS File -->
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/devicehub.css') }}" rel="stylesheet">
{% block content %}
{% endblock content %}
<!-- =======================================================
* Template Name: NiceAdmin - v2.2.0
* Template URL: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template/
* Author: BootstrapMade.com
* License: https://bootstrapmade.com/license/
======================================================== -->
</head>
</main>
</div>
</div>
<body>
{% block body %}{% endblock %}
<a href="#" class="back-to-top d-flex align-items-center justify-content-center"><i class="bi bi-arrow-up-short"></i></a>
<!-- Vendor JS Files -->
<script src="{{ url_for('static', filename='vendor/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
<!-- Template Main JS File -->
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<!-- Api backend -->
<script>
const API_URLS = {
Auth_Token: `Basic ${btoa("{{ current_user.token }}:")}`, //
currentUserID: "{{ current_user.id }}",
lots: "{# url_for('Lot.main') #}",
lots_detail: "{# url_for('inventory.lotdevicelist', lot_id='ReplaceTEXT') #}",
devices: "{{ url_for('Device.main') }}",
devices_modify: "{# url_for('Lot.lot-device', id='UUID') #}",
devices_detail: "{# url_for('inventory.device_details', id='ReplaceTEXT')#}"
}
</script>
<script src="{{ url_for('static', filename='js/api.js') }}"></script>
</body>
<!-- Footer -->
<footer class="footer text-center mt-auto py-3">
<div class="container">
<span class="text-muted">{{ commit_id }}</span>
</div>
</footer>
{% block script %}
<script src="{% static "js/jquery-3.3.1.slim.min.js" %}"></script>
<script src="{% static "js/popper.min.js" %}"></script>
<script src="{% static "js/bootstrap.min.js" %}"></script>
{% block extrascript %}{% endblock %}
{% endblock %}
</body>
</html>

View File

@ -1,213 +0,0 @@
{% load i18n static %}
<!doctype html>
<html lang="en">
<head>
{% block head %}
{% block meta %}
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="robots" content="NONE,NOARCHIVE" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="Pangea">
{% endblock %}
<title>{% block title %}{% if title %}{{ title }} {% endif %}DeviceHub{% endblock %}</title>
<!-- Bootstrap core CSS -->
{% block style %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href= "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<link href="{% static "/css/bootstrap.min.css" %}" rel="stylesheet">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
html, body {
height: 100%;
}
body {
display: flex;
flex-direction: column;
}
.main-content {
flex-grow: 1;
}
footer {
width: 100%;
}
</style>
<!-- Custom styles for this template -->
<link href="{% static "/css/dashboard.css" %}" rel="stylesheet">
{% endblock %}
{% endblock %}
</head>
<body>
<header class="navbar navbar-dark sticky-top admin bg-green flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="#">DEVICE HUB</a>
<div class="navbar-nav navbar-sub-brand">
PANGEA
</div>
<div class="navbar-nav">
<div class="nav-item text-nowrap">
<i id="user-avatar" class="bi bi-person-circle"></i>
<a class="navbar-sub-brand px-3" href="#">{{ user.email }}</a>
<a class="logout" href="{% url 'login:logout' %}">
<i class="fa-solid fa-arrow-right-from-bracket"></i>
</a>
</div>
</div>
</header>
<div class="container-fluid">
<div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="position-sticky pt-5">
<ul class="nav flex-column">
<li class="nav-item">
<a class="admin nav-link {% if section == 'Home' %}active {% endif %}fw-bold" href="{% url 'dashboard:dashboard' %}">
<i class="bi bi-house-door icon_sidebar"></i>
{% trans 'Dashboard' %}
</a>
<hr />
</li>
<li class="nav-item">
<a class="admin {% if section == 'People' %}active {% endif %}nav-link fw-bold" data-bs-toggle="collapse" data-bs-target="#people" aria-expanded="false" aria-controls="people" href="javascript:void()">
<i class="bi bi-people icon_sidebar"></i>
{% trans 'Users' %}
</a>
<ul class="flex-column mb-2 ul_sidebar accordion-collapse {% if section == 'People' %}expanded{% else %}collapse{% endif %}" id="people" data-bs-parent="#sidebarMenu">
<li class="nav-item">
<a class="nav-link{% if path == 'admin_people_list' %} active2{% endif %}" href="{# url 'idhub:admin_people_list' #}">
{% trans 'View users' %}
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == 'admin_people_new' %} active2{% endif %}" href="{# url 'idhub:admin_people_new' #}">
{% trans 'Add user' %}
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a class="admin nav-link {% if section == 'AccessControl' %}active {% endif %}fw-bold" data-bs-toggle="collapse" data-bs-target="#control-access" aria-expanded="false" aria-controls="control-access" href="javascript:void()">
<i class="fa-solid fa-arrow-right-from-bracket icon_sidebar"></i>
{% trans 'Roles' %}
</a>
<ul class="flex-column mb-2 ul_sidebar accordion-collapse {% if section == 'AccessControl' %}expanded{% else %}collapse{% endif %}" id="control-access" data-bs-parent="#sidebarMenu">
<li class="nav-item">
<a class="nav-link{% if path == 'admin_roles' %} active2{% endif %}" href="{# url 'idhub:admin_roles' #}">
{% trans 'Manage roles' %}
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == 'admin_services' %} active2{% endif %}" href="{# url 'idhub:admin_services' #}">
{% trans 'Manage services' %}
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a class="admin nav-link {% if section == 'Credential' %}active {% endif %}fw-bold" data-bs-toggle="collapse" data-bs-target="#credential" aria-expanded="false" aria-controls="credential" href="javascript:void()">
<i class="bi bi-patch-check icon_sidebar"></i>
{% trans 'Credentials' %}
</a>
<ul class="flex-column mb-2 ul_sidebar accordion-collapse {% if section == 'Credential' %}expanded{% else %}collapse{% endif %}" id="credential" data-bs-parent="#sidebarMenu">
<li class="nav-item">
<a class="nav-link{% if path == 'admin_credentials' %} active2{% endif %}" href="{# url 'idhub:admin_credentials' #}">
{% trans 'View credentials' %}
</a>
</li>
<li class="nav-item">
<a id="wallet" class="nav-link" data-bs-toggle="collapse" data-bs-target="#lwallet" aria-expanded="false" aria-controls="lwallet" href="javascript:void()">
{% trans "Organization's wallet" %}
</a>
<ul class="flex-column mb-2 accordion-collapse {% if wallet %}expanded{% else %}collapse{% endif %}" id="lwallet" data-bs-parent="#wallet">
<li class="nav-item">
<a class="nav-link{% if path == 'admin_dids' %} active2{% endif %}" href="{# url 'idhub:admin_dids' #}">
{% trans 'Manage Identities (DIDs)' %}
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == 'admin_wallet_credentials' %} active2{% endif %}" href="{# url 'idhub:admin_wallet_credentials' #}">
{% trans 'View org. credentials' %}
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == 'admin_wallet_config_issue' %} active2{% endif %}" href="{# url 'idhub:admin_wallet_config_issue' #}">
{% trans 'Configure credential issuance' %}
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="nav-item">
<a class="admin nav-link {% if section == 'Templates' %}active {% endif %}fw-bold" href="{# url 'idhub:admin_schemas' #}">
<i class="bi bi-file-earmark-text icon_sidebar"></i>
{% trans 'Templates' %}
</a>
</li>
<li class="nav-item">
<a class="admin nav-link {% if section == 'ImportExport' %}active {% endif %}fw-bold" href="{# url 'idhub:admin_import' #}">
<i class="bi bi-arrow-down-square icon_sidebar"></i>
{% trans 'Data' %}
</a>
</li>
</ul>
</div>
</nav>
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
{% block messages %}
{% for message in messages %}
<div class="alert {{ message.tags|default:'info' }} alert-dismissible fade show mt-3" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
</button>
</div>
{% endfor %}
{% endblock messages %}
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">{{ title }}</h1>
<div class="btn-toolbar mb-2 mb-md-0">
</div>
</div>
{% block content %}
{% endblock content %}
</main>
</div>
</div>
<!-- Footer -->
<footer class="footer text-center mt-auto py-3">
<div class="container">
<span class="text-muted">{{ commit_id }}</span>
</div>
</footer>
{% block script %}
<script src="{% static "js/jquery-3.3.1.slim.min.js" %}"></script>
<script src="{% static "js/popper.min.js" %}"></script>
<script src="{% static "js/bootstrap.min.js" %}"></script>
{% block extrascript %}{% endblock %}
{% endblock %}
</body>
</html>

View File

@ -0,0 +1,49 @@
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col">
<h3>{{ subtitle }}</h3>
</div>
<div class="col text-center">
<a href="{# url 'idhub:admin_people_edit' object.id #}" type="button" class="btn btn-green-admin">
<i class="bi bi-folder2"></i>
{% trans 'Lots' %}
</a>
<a href="{# url 'idhub:admin_people_edit' object.id #}" type="button" class="btn btn-green-admin">
<i class="bi bi-plus"></i>
{% trans 'Actions' %}
</a>
<a href="{# url 'idhub:admin_people_activate' object.id #}" type="button" class="btn btn-green-admin">
<i class="bi bi-reply"></i>
{% trans 'Exports' %}
</a>
<a href="#" type="button" class="btn btn-green-admin">
<i class="bi bi-tag"></i>
{% trans 'Labels' %}
</a>
</div>
</div>
<div class="dataTable-container">
<table class="table">
<thead>
<tr>
<th scope="col" data-sortable="">
<a class="dataTable-sorter" href="#">Title</a>
</th>
</tr>
</thead>
{% for dev in devices %}
<tbody>
<tr>
<td>
<a href="{% url 'device:details' dev.pk %}">{{ dev.type }} {{ dev.manufacturer }} {{ dev.model }}</a>
</td>
</tr>
</tbody>
{% endfor %}
</table>
</div>
{% endblock %}

View File

@ -1,8 +1,8 @@
from django.urls import path
from dashboard.views import DashboardView
from dashboard import views
app_name = 'dashboard'
urlpatterns = [
path("", DashboardView.as_view(), name="dashboard"),
path("", views.UnassignedDevicesView.as_view(), name="unassigned_devices"),
]

View File

@ -1,46 +1,19 @@
from django.views import View
from django.template.loader import get_template
from django.http import HttpResponse
from django.urls import resolve
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import PermissionDenied
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.base import TemplateView
from dashboard.mixins import DashboardView
from device.models import Device
class Http403(PermissionDenied):
status_code = 403
default_detail = _('Permission denied. User is not authenticated')
default_code = 'forbidden'
def __init__(self, details=None, code=None):
if details is not None:
self.detail = details or self.default_details
if code is not None:
self.code = code or self.default_code
class DashboardView(LoginRequiredMixin, View):
login_url = "/login/"
template_name = "dashboard.html"
def get(self, request, *args, **kwargs):
template = get_template(
self.template_name,
).render()
return HttpResponse(template)
# response = super().get(request, *args, **kwargs)
# return response
class UnassignedDevicesView(DashboardView, TemplateView):
template_name = "unassigned_devices.html"
section = "Unassigned"
title = _("Unassigned Devices")
breadcrumb = "Devices / Unassigned Devices"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
devices = Device.objects.filter(owner=self.request.user)
context.update({
# 'title': self.title,
# 'subtitle': self.subtitle,
# 'icon': self.icon,
# 'section': self.section,
'path': resolve(self.request.path).url_name,
# 'user': self.request.user,
'devices': devices,
})
return context

42
device/forms.py Normal file
View File

@ -0,0 +1,42 @@
from django import forms
# from django.utils.translation import gettext_lazy as _
# from django.core.exceptions import ValidationError
# from user.models import User
from device.models import (
Device,
PhysicalProperties
)
class DeviceForm(forms.ModelForm):
class Meta:
model = Device
fields = [
'type',
"model",
"manufacturer",
"serial_number",
"part_number",
"brand",
"generation",
"version",
"production_date",
"variant",
"family",
]
class PhysicalPropsForm(forms.Form):
class Meta:
model = PhysicalProperties
fields = [
"device",
"weight",
"width",
"height",
"depth",
"color",
"image",
]

View File

@ -0,0 +1,33 @@
# Generated by Django 5.0.6 on 2024-07-03 11:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("device", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="device",
name="brand",
field=models.CharField(blank=True, max_length=64, null=True),
),
migrations.AlterField(
model_name="device",
name="devicehub_id",
field=models.CharField(blank=True, max_length=64, null=True, unique=True),
),
migrations.AlterField(
model_name="device",
name="variant",
field=models.CharField(blank=True, max_length=64, null=True),
),
migrations.AlterField(
model_name="device",
name="version",
field=models.CharField(blank=True, max_length=64, null=True),
),
]

View File

@ -0,0 +1,73 @@
# Generated by Django 5.0.6 on 2024-07-03 12:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("device", "0002_alter_device_brand_alter_device_devicehub_id_and_more"),
]
operations = [
migrations.RemoveField(
model_name="component",
name="type",
),
migrations.RemoveField(
model_name="computer",
name="type",
),
migrations.RemoveField(
model_name="datastorage",
name="type",
),
migrations.AlterField(
model_name="computer",
name="chassis",
field=models.CharField(
blank=True,
choices=[
("Tower", "Tower"),
("All in one", "Allinone"),
("Microtower", "Microtower"),
("Netbook", "Netbook"),
("Laptop", "Laptop"),
("Tablet", "Tabler"),
("Server", "Server"),
("Non-physical device", "Virtual"),
],
max_length=32,
null=True,
),
),
migrations.AlterField(
model_name="computer",
name="sku",
field=models.CharField(blank=True, max_length=32, null=True),
),
migrations.AlterField(
model_name="device",
name="type",
field=models.CharField(
choices=[
("Desktop", "Desktop"),
("Laptop", "Laptop"),
("Server", "Server"),
("GraphicCard", "Graphiccard"),
("HardDrive", "Harddrive"),
("SolidStateDrive", "Solidstatedrive"),
("Motherboard", "Motherboard"),
("NetworkAdapter", "Networkadapter"),
("Processor", "Processor"),
("RamModule", "Rammodule"),
("SoundCard", "Soundcard"),
("Display", "Display"),
("Battery", "Battery"),
("Camera", "Camera"),
],
default="Laptop",
max_length=32,
),
),
]

View File

@ -3,23 +3,36 @@ from user.models import User
from utils.constants import STR_SM_SIZE, STR_SIZE
# Create your models here.
class Device(models.Model):
class Types(models.TextChoices):
DESKTOP = "Desktop"
LAPTOP = "Laptop"
SERVER = "Server"
GRAPHICCARD = "GraphicCard"
HARDDRIVE = "HardDrive"
SOLIDSTATEDRIVE = "SolidStateDrive"
MOTHERBOARD = "Motherboard"
NETWORKADAPTER = "NetworkAdapter"
PROCESSOR = "Processor"
RAMMODULE = "RamModule"
SOUNDCARD = "SoundCard"
DISPLAY = "Display"
BATTERY = "Battery"
CAMERA = "Camera"
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
type = models.CharField(max_length=STR_SM_SIZE)
type = models.CharField(max_length=STR_SM_SIZE, choices=Types, default=Types.LAPTOP)
model = models.CharField(max_length=STR_SIZE, blank=True, null=True)
manufacturer = models.CharField(max_length=STR_SIZE, blank=True, null=True)
serial_number = models.CharField(max_length=STR_SIZE, blank=True, null=True)
part_number = models.CharField(max_length=STR_SIZE, blank=True, null=True)
brand = models.TextField(blank=True, null=True)
brand = models.CharField(max_length=STR_SIZE, blank=True, null=True)
generation = models.SmallIntegerField(blank=True, null=True)
version = models.TextField(blank=True, null=True)
version = models.CharField(max_length=STR_SIZE, blank=True, null=True)
production_date = models.DateTimeField(blank=True, null=True)
variant = models.TextField(blank=True, null=True)
devicehub_id = models.TextField(unique=True, blank=True, null=True)
variant = models.CharField(max_length=STR_SIZE, blank=True, null=True)
devicehub_id = models.CharField(max_length=STR_SIZE, unique=True, blank=True, null=True)
dhid_bk = models.CharField(max_length=STR_SIZE, blank=True, null=True)
phid_bk = models.CharField(max_length=STR_SIZE, blank=True, null=True)
family = models.CharField(max_length=STR_SIZE, blank=True, null=True)
@ -40,33 +53,30 @@ class PhysicalProperties(models.Model):
class Computer(models.Model):
class Types(models.TextChoices):
DESKTOP = "Desktop"
LAPTOP = "Laptop"
class Chassis(models.TextChoices):
TOWER = 'Tower'
ALLINONE = 'All in one'
MICROTOWER = 'Microtower'
NETBOOK = 'Netbook'
LAPTOP = 'Laptop'
TABLER = 'Tablet'
SERVER = "Server"
VIRTUAL = 'Non-physical device'
device = models.OneToOneField(Device, models.CASCADE, primary_key=True)
chassis = models.TextField(blank=True, null=True)
chassis = models.CharField(
blank=True,
null=True,
max_length=STR_SM_SIZE,
choices=Chassis
)
system_uuid = models.UUIDField()
sku = models.TextField(blank=True, null=True)
type = models.CharField(max_length=STR_SM_SIZE, choices=Types, default=Types.LAPTOP)
sku = models.CharField(max_length=STR_SM_SIZE, blank=True, null=True)
class Component(models.Model):
class Types(models.TextChoices):
GRAPHICCARD = "GraphicCard"
DATASTORAGE = "DataStorage"
MOTHERBOARD = "Motherboard"
NETWORKADAPTER = "NetworkAdapter"
PROCESSOR = "Processor"
RAMMODULE = "RamModule"
SOUNDCARD = "SoundCard"
DISPLAY = "Display"
BATTERY = "Battery"
CAMERA = "Camera"
device = models.OneToOneField(Device, models.CASCADE, primary_key=True)
type = models.CharField(max_length=STR_SM_SIZE, choices=Types)
computer = models.OneToOneField(Computer, models.CASCADE, null=True)
@ -82,14 +92,9 @@ class DataStorage(models.Model):
PCI = 'PCI'
NVME = 'NVME'
class Type(models.TextChoices):
HARDDRIVE = "HardDrive"
SOLIDSTATEDRIVE = "SolidStateDrive"
component = models.OneToOneField(Component, models.CASCADE)
size = models.IntegerField(blank=True, null=True)
interface = models.CharField(max_length=STR_SM_SIZE, choices=Interface)
type = models.CharField(max_length=STR_SM_SIZE, choices=Type)
component = models.OneToOneField(Component, models.CASCADE)
class Motherboard(models.Model):

View File

@ -0,0 +1,262 @@
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col">
<h3>{{ object.pk }}</h3>
</div>
</div>
<div class="row">
<div class="col">
<div class="nav nav-tabs nav-tabs-bordered">
<li class="nav-items">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#details">General details</button>
</li>
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#physicalproperties">Physical properties</button>
</li>
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#documents">Documents</button>
</li>
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#lots">Lots</button>
</li>
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#status">Status</button>
</li>
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#components">Components</button>
</li>
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#traceabiliy">Traceability log</button>
</li>
<li class="nav-items">
<a class="nav-link" href="">Web</a>
</li>
</div>
</div>
</div>
<div class="tab-content pt-2">
<div class="tab-pane fade show active" id="details">
<h5 class="card-title">Details</h5>
<div class="row mb-3">
<div class="col-lg-3 col-md-4 label ">
(<a href="/inventory/device/edit/4W8D3/">Edit Device</a>)
</div>
<div class="col-lg-9 col-md-8">
{%if object.hid %}Snapshot{% else %}Placeholder{% endif %}
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">Phid</div>
<div class="col-lg-9 col-md-8">{{ object.id }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">Id device internal</div>
<div class="col-lg-9 col-md-8"></div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">Type</div>
<div class="col-lg-9 col-md-8">{{ object.type }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Manufacturer</div>
<div class="col-lg-9 col-md-8">{{ object.manufacturer|default:"" }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Model</div>
<div class="col-lg-9 col-md-8">{{ object.model|default:"" }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Part Number</div>
<div class="col-lg-9 col-md-8">{{ object.part_number|default:"" }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Serial Number</div>
<div class="col-lg-9 col-md-8">{{ object.serial_number|default:"" }}</div>
</div>
</div>
<div class="tab-pane fade profile-overview" id="physicalproperties">
<h5 class="card-title">Physical Properties</h5>
<div class="row mb-3">
<div class="col-lg-3 col-md-4 label ">
<a href="{% url 'device:details' object.pk %}">{{ object.type }} {{ object.manufacturer }} {{ object.model }}</a>
<br>
<a href="{% url 'device:physical_edit' object.pk %}">{{ object.type }} {{ object.manufacturer }} {{ object.model }}</a>
<br>
(<a href="{# url('device:physical_edit', object.id) #}">Edit Physical Properties</a>)
</div>
</div>
<div class="row mb-3">
<div class="col-lg-3 col-md-4 label ">
Weight:
</div>
<div class="col-lg-9 col-md-8">
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">width:</div>
<div class="col-lg-9 col-md-8"></div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">height:</div>
<div class="col-lg-9 col-md-8"></div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">depth:</div>
<div class="col-lg-9 col-md-8"></div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">color:</div>
<div class="col-lg-9 col-md-8"></div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">image:</div>
<div class="col-lg-9 col-md-8"></div>
</div>
</div>
<div class="tab-pane fade profile-overview" id="lots">
<h5 class="card-title">Incoming Lots</h5>
<div class="row">
</div>
<h5 class="card-title">Outgoing Lots</h5>
<div class="row">
</div>
<h5 class="card-title">Temporary Lots</h5>
<div class="row">
</div>
</div>
<div class="tab-pane fade profile-overview" id="documents">
<div class="btn-group dropdown ml-1 mt-1" uib-dropdown="">
<a href="/inventory/device/4W8D3/document/add/" class="btn btn-primary">
<i class="bi bi-plus"></i>
Add new document
<span class="caret"></span>
</a>
</div>
<h5 class="card-title">Documents</h5>
<table class="table">
<thead>
<tr>
<th scope="col">File</th>
<th scope="col">Type</th>
<th scope="col">Description</th>
<th scope="col" data-type="date" data-format="YYYY-MM-DD hh:mm">Uploaded on</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="tab-pane fade profile-overview" id="status">
<h5 class="card-title">Status Details</h5>
<div class="row">
<div class="col-lg-3 col-md-4 label">Physical State</div>
<div class="col-lg-9 col-md-8">
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Lifecycle State</div>
<div class="col-lg-9 col-md-8">
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Allocated State</div>
<div class="col-lg-9 col-md-8">
</div>
</div>
</div>
<div class="tab-pane fade profile-overview" id="traceability">
<h5 class="card-title">Traceability log Details</h5>
<div class="list-group col-6">
<div class="list-group-item d-flex justify-content-between align-items-center">
Snapshot ✓
<small class="text-muted">14:07 23-06-2024</small>
</div>
<div class="list-group-item d-flex justify-content-between align-items-center">
EraseCrypto ✓
<small class="text-muted">14:07 23-06-2024</small>
</div>
<div class="list-group-item d-flex justify-content-between align-items-center">
EraseCrypto ✓
<small class="text-muted">14:07 23-06-2024</small>
</div>
</div>
</div>
<div class="tab-pane fade profile-overview" id="components">
<h5 class="card-title">Components Snapshot</h5>
<div class="list-group col-6">
<div class="list-group-item">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Motherboard</h5>
<small class="text-muted">14:07 23-06-2024</small>
</div>
<p class="mb-1">
hp<br />
890e<br />
</p>
<small class="text-muted">
</small>
</div>
<div class="list-group-item">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">NetworkAdapter</h5>
<small class="text-muted">14:07 23-06-2024</small>
</div>
<p class="mb-1">
realtek semiconductor co., ltd.<br />
rtl8852ae 802.11ax pcie wireless network adapter<br />
</p>
<small class="text-muted">
</small>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,32 @@
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col">
<h3>{{ subtitle }}</h3>
</div>
</div>
{% load django_bootstrap5 %}
<form role="form" method="post">
{% csrf_token %}
{% if form.errors %}
<div class="alert alert-danger alert-icon alert-icon-border alert-dismissible" role="alert">
<div class="icon"><span class="mdi mdi-close-circle-o"></span></div>
<div class="message">
{% for field, error in form.errors.items %}
{{ error }}<br />
{% endfor %}
<button class="btn-close" type="button" data-dismiss="alert" aria-label="Close"></button>
</div>
</div>
{% endif %}
{% bootstrap_form form %}
<div class="form-actions-no-box">
<a class="btn btn-grey" href="{% url 'dashboard:unassigned_devices' %}">{% translate "Cancel" %}</a>
<input class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Save' %}" />
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,246 @@
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col">
<h3>{{ object.pk }}</h3>
</div>
</div>
<div class="row">
<div class="col">
<div class="nav nav-tabs nav-tabs-bordered">
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#details">General details</button>
</li>
<li class="nav-items">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#physicalproperties">Physical properties</button>
</li>
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#documents">Documents</button>
</li>
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#lots">Lots</button>
</li>
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#status">Status</button>
</li>
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#components">Components</button>
</li>
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#traceabiliy">Traceability log</button>
</li>
<li class="nav-items">
<a class="nav-link" href="">Web</a>
</li>
</div>
</div>
</div>
<div class="tab-content pt-2">
<div class="tab-pane fade profile-overview" id="details">
<h5 class="card-title">Details</h5>
<div class="row mb-3">
<div class="col-lg-3 col-md-4 label ">
(<a href="/inventory/device/edit/4W8D3/">Edit Device</a>)
</div>
<div class="col-lg-9 col-md-8">
{%if object.hid %}Snapshot{% else %}Placeholder{% endif %}
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">Phid</div>
<div class="col-lg-9 col-md-8">{{ object.id }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">Id device internal</div>
<div class="col-lg-9 col-md-8"></div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">Type</div>
<div class="col-lg-9 col-md-8">{{ object.type }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Manufacturer</div>
<div class="col-lg-9 col-md-8">{{ object.manufacturer|default:"" }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Model</div>
<div class="col-lg-9 col-md-8">{{ object.model|default:"" }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Part Number</div>
<div class="col-lg-9 col-md-8">{{ object.part_number|default:"" }}</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Serial Number</div>
<div class="col-lg-9 col-md-8">{{ object.serial_number|default:"" }}</div>
</div>
</div>
<div class="tab-pane fade show active" id="physicalproperties">
<h5 class="card-title">Physical Properties</h5>
<div class="row mb-3">
<div class="col-lg-3 col-md-4 label ">
<a href="{% url 'device:details' object.pk %}">{{ object.type }} {{ object.manufacturer }} {{ object.model }}</a>
<br>
<a href="{% url 'device:physical_edit' object.pk %}">{{ object.type }} {{ object.manufacturer }} {{ object.model }}</a>
<br>
(<a href="{# url('device:physical_edit', object.id) #}">Edit Physical Properties</a>)
</div>
</div>
<div class="row mb-3">
<div class="col-lg-3 col-md-4 label ">
{{ form }}
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">color:</div>
<div class="col-lg-9 col-md-8"></div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">image:</div>
<div class="col-lg-9 col-md-8"></div>
</div>
</div>
<div class="tab-pane fade profile-overview" id="lots">
<h5 class="card-title">Incoming Lots</h5>
<div class="row">
</div>
<h5 class="card-title">Outgoing Lots</h5>
<div class="row">
</div>
<h5 class="card-title">Temporary Lots</h5>
<div class="row">
</div>
</div>
<div class="tab-pane fade profile-overview" id="documents">
<div class="btn-group dropdown ml-1 mt-1" uib-dropdown="">
<a href="/inventory/device/4W8D3/document/add/" class="btn btn-primary">
<i class="bi bi-plus"></i>
Add new document
<span class="caret"></span>
</a>
</div>
<h5 class="card-title">Documents</h5>
<table class="table">
<thead>
<tr>
<th scope="col">File</th>
<th scope="col">Type</th>
<th scope="col">Description</th>
<th scope="col" data-type="date" data-format="YYYY-MM-DD hh:mm">Uploaded on</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="tab-pane fade profile-overview" id="status">
<h5 class="card-title">Status Details</h5>
<div class="row">
<div class="col-lg-3 col-md-4 label">Physical State</div>
<div class="col-lg-9 col-md-8">
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Lifecycle State</div>
<div class="col-lg-9 col-md-8">
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Allocated State</div>
<div class="col-lg-9 col-md-8">
</div>
</div>
</div>
<div class="tab-pane fade profile-overview" id="traceability">
<h5 class="card-title">Traceability log Details</h5>
<div class="list-group col-6">
<div class="list-group-item d-flex justify-content-between align-items-center">
Snapshot ✓
<small class="text-muted">14:07 23-06-2024</small>
</div>
<div class="list-group-item d-flex justify-content-between align-items-center">
EraseCrypto ✓
<small class="text-muted">14:07 23-06-2024</small>
</div>
<div class="list-group-item d-flex justify-content-between align-items-center">
EraseCrypto ✓
<small class="text-muted">14:07 23-06-2024</small>
</div>
</div>
</div>
<div class="tab-pane fade profile-overview" id="components">
<h5 class="card-title">Components Snapshot</h5>
<div class="list-group col-6">
<div class="list-group-item">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Motherboard</h5>
<small class="text-muted">14:07 23-06-2024</small>
</div>
<p class="mb-1">
hp<br />
890e<br />
</p>
<small class="text-muted">
</small>
</div>
<div class="list-group-item">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">NetworkAdapter</h5>
<small class="text-muted">14:07 23-06-2024</small>
</div>
<p class="mb-1">
realtek semiconductor co., ltd.<br />
rtl8852ae 802.11ax pcie wireless network adapter<br />
</p>
<small class="text-muted">
</small>
</div>
</div>
</div>
</div>
{% endblock %}

10
device/urls.py Normal file
View File

@ -0,0 +1,10 @@
from django.urls import path
from device import views
app_name = 'device'
urlpatterns = [
path("add/", views.NewDeviceView.as_view(), name="add"),
path("<int:pk>/", views.DetailsView.as_view(), name="details"),
path("physical/<int:pk>/", views.PhysicalView.as_view(), name="physical_edit"),
]

View File

@ -1,3 +1,81 @@
from django.shortcuts import render
from django.urls import reverse_lazy
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
from django.views.generic.edit import (
CreateView,
UpdateView,
)
from dashboard.mixins import DashboardView, DetailsMixin
from device.forms import DeviceForm, PhysicalPropsForm
from device.models import Device, PhysicalProperties
class NewDeviceView(DashboardView, CreateView):
template_name = "new_device.html"
title = _("New Device")
breadcrumb = "Device / New Device"
form_class = DeviceForm
success_url = reverse_lazy('dashboard:unassigned_devices')
def form_valid(self, form):
form.instance.owner = self.request.user
response = super().form_valid(form)
PhysicalProperties.objects.create(device=form.instance)
return response
class DetailsView(DetailsMixin):
template_name = "details.html"
title = _("Device")
breadcrumb = "Device / Details"
model = Device
class PhysicalView(DashboardView, UpdateView):
template_name = "physical_properties.html"
title = _("Physical Properties")
breadcrumb = "Device / Physical properties"
form_class = PhysicalPropsForm
success_url = reverse_lazy('dashboard:unassigned_devices')
model = PhysicalProperties
def get(self, request, *args, **kwargs):
pk = kwargs['pk']
self.device = get_object_or_404(Device, pk=pk)
try:
self.object = self.device.physicalproperties
except Exception:
self.object = PhysicalProperties.objects.create(device=self.device)
self.initial.update({'instance': self.object})
return super().get(request, *args, **kwargs)
def get_form(self, form_class=None):
"""Return an instance of the form to be used in this view."""
if form_class is None:
form_class = self.get_form_class()
# import pdb; pdb.set_trace()
return form_class(**self.get_form_kwargs())
def get_form_kwargs(self):
"""Return the keyword arguments for instantiating the form."""
kwargs = {
"initial": self.get_initial(),
"prefix": self.get_prefix(),
}
if self.request.method in ("POST", "PUT"):
kwargs.update(
{
"data": self.request.POST,
"files": self.request.FILES,
}
)
return kwargs
def form_valid(self, form):
self.success_url = reverse_lazy('device:details', self.device.id)
form.instance.owner = self.request.user
response = super().form_valid(form)
return response
# Create your views here.

View File

@ -21,4 +21,5 @@ urlpatterns = [
# path('api/', include('snapshot.urls')),
path("", include("login.urls")),
path("dashboard/", include("dashboard.urls")),
path("device/", include("device.urls")),
]