GVKun编程网logo

Spring学习笔记(11)——bean之间的关系(spring和bean的关系)

22

对于Spring学习笔记感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解11——bean之间的关系,并且为您提供关于Django学习笔记(11)——开发图书管理页面、Django学习笔记(1

对于Spring学习笔记感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解11——bean之间的关系,并且为您提供关于Django学习笔记(11)——开发图书管理页面、Django学习笔记(17)——BBS+Blog项目开发(1)验证码功能的实现、Golang 学习笔记(03)——os包(1)、JavaEE_SSH_Spring学习笔记(9)----让Spring自动扫描和管理Bean的宝贵知识。

本文目录一览:

Spring学习笔记(11)——bean之间的关系(spring和bean的关系)

Spring学习笔记(11)——bean之间的关系(spring和bean的关系)

1)继承:

如果多个bean存在相同的配置信息,Spring允许我们定义一个父,子将自动继承父的配置信息。

如下:

<bean class="com.baobaobao.Car" id="car1" p:color="红色"p:price="200000" p:brand="奥迪">
<bean class="com.baobaobao.Car" id="car2" p:color="白色"p:price="200000"p:brand="奥迪"></bean></bean>

 

这两个bean的配置存在大量重复信息,可以通过父子bean很好的消除这种重复配置:

<beanclass="com.baobaobao.Car"id="abstractcar"p:color="黑色"p:price="200000"p:brand="奥迪"abstract="true">
<bean id="car1"p:color="红色"parent="abstractcar">
<bean id="car2"p:color="白色"parent="abstractcar">
</bean>
</bean></bean>
 

Car1跟Car2都继承自abstractcar,Spring会将父bean的配置信息传递给子bean,如果子bean提供了父bean已有的配置信息,那么子bean的会覆盖父bean的

 

父bean的功能主要是为了简化子bean的配置,所以一般声明为abstract=“true”,表示这个不实例化为一个对应的Bean,如果用户不指定该属性为true,那么IOC容器会实例化一个名叫abstractcar的Bean。

 

2)依赖

一般情况使用即可建立一个bean对其他bean的依赖关系,Spring负责管理这些Bean的关系,当实例化一个Bean时,Spring保证该Bean所依赖的其他Bean已经初始化。

书上举了一个比较经典而特殊的例子:

例如一个系统,有一个SystemSetting类,

 
 
 
 
 
 
publicclassSystemSetting {
    publicstaticint SESSION_TIMEOUT=30;
    publicstaticint REFRESH_CYCLE=60;
    .....}

里面有一些系统参数的默认值。

而这个系统有个管理后台,可以修改这些值保存到数据库中,而有一个SysInit类,在初始化的时候从数据中拿到值,修改SystemSetting的参数

 
 
 
 
 
publicclassSysInit {
    publicSysInit(){
        SystemSetting.SESSION_TIMEOUT=100;
        SystemSetting.REFRESH_CYCLE=120;
    }
}


假设这个系统有一个缓存刷新管理器,它需要根据系统参数SystemSetting.REFRESH_CYCLE创建缓存刷新定时任务:

 
 
 
 
 
publicclassCacheManager {
    publicCacheManager(){
        Timer timer =newTimer();
        TimerTask cacheTask =newCacheTask();
        timer.schedule(cacheTask,0,SystemSetting.REFRESH_CYCLE);
    }
}


在上面的例子中CacheManager依赖于SystemSetting,而SystemSetting的值由SysInit负责初始化,虽然CacheManager不直接依赖于SysInit,但是,从逻辑上讲,CacheManager希望在SysInit加载并完成系统参数设置后再启动,以避免调用不到真实的参数值。如果这三个Bean都在配置中配,如何保证SysInit在CacheManager之前初始化呢

 

使用depends-on属性可以指定Bean的前置依赖Bean,前置依赖Bean会在本Bean实例化之前创建好:

 

 
 
<bean class="com.baobaobao.CacheManager"id="manager"depends-on="sysinit">
     <bean class="com.baobaobao.SysInit"id="sysinit">
</bean></bean>


这样配置就哦了,如果前置依赖于多个bean,可以通过逗号,空格,或分号分隔开来。

  

3)引用

假如一个bean要引用另一个bean的配置的id属性值,可以使用如下配置:

 

 
 
 
 
 
<beanclass="com.baobaobao.Car"id="car">
    <beanclass="com.baobaobao.Boss"id="boss">
        <property name="carId">
            <idref bean="car">
        </idref>
    </property></bean></bean>

 

如果引用者与被引用者位于同一个配置文件中,可以使用,这是IDE的XML分析器就可以在开发期发现引用错误了。

 

      Java实例的属性值可以有很多种数据类型、基本类型值、字符串类型、java实例甚至其他的Bean实例、java集合、数组等。所以Spring允许通过如下几个元素为Bean实例的属性指定值:

          value

          ref

          bean

          list、set、map、props

 

          一、value:设置普通属性值

          <value.../>元素用于指定字符串类型、基本类型的属性值。Spring使用XML解析器来解析出这些数据,然后利用java.beans.PropertyEdior完成类型转换:从java.lang.String类型转换为所需的参数值类型。如果目标类型是基本数据类型,通常都是可以正确转换。

在Spring配置文件中使用<value.../>元素来为这两个属性指定属性值。

 

1 <bean id="text" class="com.spring.service.impl.ValueTest">
2         <property name="age" value="1" />
3         <property name="name" value="chenssy" />
4     </bean>

 

          通过上面可以知道<value.../>元素主要用于传入字符串、基本类型的属性值。

 

          二、ref:配置合作者

          <value.../>主要是配置基本类型的属性值,但是如果我们需要为Bean设置属性值是另一个Bean实例时,这个时候需要使用<ref.../>元素。使用<ref.../>元素可以指定如下两个属性。

          bean:引用不在同一份XML配置文件中的其他Bean实例的id属性值。

          local:引用同一份XML配置文件中的其他Bean实例的id属性值。

 

1 <bean id="steelAxe" class="com.spring.service.impl.SteelAce"></bean>
2     <bean id="chinese" class="com.spring.service.impl.Chinese" >
3         <property name="axe">
4             <ref local="steelAxe"/>
5         </property>
6     </bean>
 
          其实Spring提供了一种更加简洁的写法:  
1 <bean id="steelAxe" class="com.spring.service.impl.SteelAce"></bean>
2     <bean id="chinese" class="com.spring.service.impl.Chinese" >
3         <property name="axe" ref="steelAxe" />
4     </bean>

 

          通过property增加ref属性,一样可以将另一个Bean的引用设置成axe属性值。这样写的效果和使用<ref.../>属性一样,而且不需要区分是使用bean属性还是local属性,所以推荐这种写法。

 

          2.1、使用自动装配注入合作者bean

          Spring支持自动装配Bean与Bean之间的依赖关系,也就是说我们无需显示的指定依赖Bean。由BeanFactory检查XML配置文件内容,根据某种规则,为主调Bean注入依赖关系。

Spring的自动装配机制可以通过<bean.../>元素的default-autowire属性指定,也可以通过<bean.../>元素的autowire属性指定。

          自动装配可以减少配置文件的工作量,但是它降低了依赖关系的透明性和清晰性,所以一般来说在较大部署环境中不推荐使用,显示配置合作者能够得到更加清晰的依赖关系。Spring提供了如下几种规则来实现自动装配。

          no:不适用自动装配。Bean依赖必须通过ref元素定义。

          byName:根据属性名自动装配。BeanFactory查找容器中的全部Bean,找出其中id属性与属性同名的Bean来完成注入。如果没有找到匹配的Bean实例,则Spring不会进行任何注入。

          byType:根据属性类型自动装配。BeanFactory查找容器中的全部Bean,如果正好有一个与依赖属性类型相同的Bean,就自动注入这个属性;但是如果有多个这样的Bean,就会抛出一个异常。如果没有匹配的Bean,则什么都不会发生,属性就不会被设置。如果需要无法自动装配时抛出异常,则设置dependency-check=”objects”。

          constructor:与不Type类似,区别是用于构造注入的参数。

          Autodetect:BeanFactory根据Bean内部结构,决定使用constructor或者byType。如果找到一个默认的构造函数,则使用byTe。

           

          byName规则

          byTyep规则是指通过名字注入依赖关系,假如Bean A的实现类里面包含setB()方法,而Spring的配置文件恰好包含一个id为b的Bean,则Spring容器就会将b实例注入Bean A中。如果容器中没有名字匹配的Bean,Spring则不会做任何事情。

 

1 <bean id="chinese" class="com.spring.service.impl.Chinese" autowire="byName" />
2     <bean id="gundog" class="com.spring.service.impl.Gundog">
3         <property name="name" value="wangwang" />
4     </bean>

 

          上面的配置文件指定了byName规则。则com.app.service.impl.Chinese类中提供如下的依赖注入方法:

 

 
1 /* 2  * 依赖关系必须的setter方法,因为需要通过名字自动装配 3  * 所以setter方法必须提供set+Bean名,Bean名的首字母大写 4  * @param dog 设置的dog值 5  */ 6  public void setGundog(Dog dog){ 7  this.dog = dog; 8     }
 

 

          byType规则

          byType规则是根据类型匹配注入依赖关系。假如A实例有setB(B b)方法,而Spring配置文件中恰好有一个类型B的Bean实例,容器为A注入类型匹配的Bean实例。如果容器中存在多个B的实例,则会抛出异常,如果没有B实例,则不会发生任何事情。

 

1 <bean id="chinese" class="com.spring.service.impl.Chinese" autowire="byType" />
2     <bean id="gundog" class="com.spring.service.impl.Gundog">
3         <property name="name" value="wangwang" />
4     </bean>

 

          针对上面的配置文件Chinese类有如下方法。

 

1 /** 2  * 依赖关系必须的setter方法 3  * 因为使用按类型自动装配,setter方法的参数类型与容器的Bean的类型相同 4  * 程序中的Gundog实现了Dog接口 5  * @param dog传入的dog对象 6  */ 7  public void setDog(Dog dog){ 8  this.dog = dog; 9     } 

          当一个Bean即使用自动装配依赖,又使用ref显示依赖时,则显示指定的依赖就会覆盖自动装配。

          在默认的情况下,Spring会自动搜索容器中的全部Bean,并对这些Bean进行判断,判断他们是否满足自动装配的条件,如果满足就会将该Bean注入目标Bean实例中。如果我们不想让Spring搜索容器中的全部Bean,也就是说,我们需要Spring来判断哪些Bean需要搜索,哪些Bean不需要搜索,这个时候就需要用到autowire-candidate属性。通过为<bean.../>元素设置autowire-candidate=”false”,即可将该Bean限制在自动装配范围之外,容器在查找自动装配对象时将不考虑该Bean。

 

          三、Bean:注入嵌套Bean

          如果某个Bean所依赖的Bean不想被Spring容器直接访问,则可以使用嵌套Bean。<bean.../>元素用来定义嵌套Bean,嵌套Bean只对嵌套它的外部Bean有效,Spring容器无法直接访问嵌套Bean,因此在定义嵌套Bean时是无需指定id属性的。

 

1 <bean id="chinese" class="com.spring.service.impl.Chinese" autowire="byName">
2         <property name="axe">
3             <!-- 
4  属性值为嵌套Bean,嵌套Bean不能由Spring容器直接访问, 5  所以嵌套Bean是不需要id属性 6              -->
7             <bean class="com.spring.service.impl.SteelAce" />
8         </property>
9     </bean>
            采用上面的配置可以保证嵌套Bean不能被容器访问,因此不用担心其他程序修改嵌套bean。但是嵌套Bean限制了Bean的访问,提高了程序的内聚性。

 

          四、list、set、map、props

          <value.../>元素是注入基本数据类型和String类型的,但是如果某个Bean的属性是集合呢?这个时候我们就需要使用集合元素,<list.../>、<set.../>、<map.../>和<props.../>元素分别用来设置类型list、set、map和Properties的集合属性值。

          先看下面java类:

 

 1 public class Chinese implements Person{  2 
 3     //下面是一系列的集合属性
 4     private List<String> schools;  5     private Map scores;  6     private Map<String, Axe> phaseAxes;  7     private Properties health;  8     private Set axe;  9     private String[] books; 10     
11     public List<String> getSchools() { 12         return schools; 13  } 14 
15     public void setSchools(List<String> schools) { 16         this.schools = schools; 17  } 18 
19     public Map getScores() { 20         return scores; 21  } 22 
23     public void setScores(Map scores) { 24         this.scores = scores; 25  } 26 
27     public Map<String, String> getPhaseAxes() { 28         return phaseAxes; 29  } 30 
31     public void setPhaseAxes(Map<String, String> phaseAxes) { 32         this.phaseAxes = phaseAxes; 33  } 34 
35     public Properties getHealth() { 36         return health; 37  } 38 
39     public void setHealth(Properties health) { 40         this.health = health; 41  } 42 
43     public Set getAxe() { 44         return axe; 45  } 46 
47     public void setAxe(Set axe) { 48         this.axe = axe; 49  } 50 
51     public String[] getBooks() { 52         return books; 53  } 54 
55     public void setBooks(String[] books) { 56         this.books = books; 57  } 58 
59     public void useAxe() { 60         
61  } 62 }
             上面的java代码中有数组、list、set、,map、Properties。下面是针对上面的配置文件。

 

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3  xmlns="http://www.springframework.org/schema/beans"
 4  xsi:schemaLocation="http://www.springframework.org/schema/beans  5  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 6     <!-- 定义一个普通的Axe Bean -->
 7     <bean id="steelAxe" class="com.spring.service.impl.SteelAxe" />
 8     <bean id="stoneAxe" class="com.spring.service.impl.StoneAxe" />
 9     
10     <!--定义Chinese Bean -->
11     <bean id="chinese" class="com.spring.service.impl.Chinese">
12         <property name="schools">
13             <list>
14                 <value>小学</value>
15                 <value>中学</value>
16                 <value>大学</value>
17             </list>
18         </property>
19         
20         <property name="scores">
21             <map>
22                 <entry key="语文" value="88" />
23                 <entry key="数学" value="87" />
24                 <entry key="外语" value="88" />
25             </map>
26         </property>
27         
28         <property name="phaseAxes">
29             <map>
30                 <entry key="原始社会" value-ref="stoneAxe" />
31                 <entry key="农业社会" value-ref="steelAxe" />
32             </map>
33         </property>
34         
35         <property name="health">
36             <props>
37                 <prop key="血压">正常</prop>
38                 <prop key="身高">175</prop>
39             </props>
40         </property>
41         
42         <property name="axe">
43             <set>
44                 <value>普通字符串</value>
45                 <bean class="com.spring.service.impl.SteelAxe"></bean>
46                 <ref local="stoneAxe"/>
47             </set>
48         </property>
49         
50         <property name="books">
51             <list>
52                 <value>java 编程思想</value>
53                 <value>思考致富</value>
54                 <value>将才</value>
55             </list>
56         </property>
57     </bean>
58 </beans>
 

 

       从上面的配置文件中可以看出,Spring对list属性和数组属性的处理是一样的。

当我们使用<list.../>、<set.../>、<map.../>等元素配置集合属性时,我们还需要手动配置集合元素。由于集合元素又可以是基本类型值、引用容器中的其他Bean、嵌套Bean和集合属性等。所以这些元素又可以接受如下子元素:

          value:指定集合元素是基本数据类型或者字符类型值。

          ref:指定集合元素师容器中另一个Bean实例。

          bean:指定集合元素是一个嵌套Bean。

          list、set、map、props:指定集合元素值又是集合。

 

http://blog.csdn.net/u011225629/article/details/45460209

Django学习笔记(11)——开发图书管理页面

Django学习笔记(11)——开发图书管理页面

一,项目题目: 开发图书管理页面

  该项目主要练习Django对多个数据库进行增删改查的操作。

二,项目需求:

基础需求:75%
1. 列出图书列表、出版社列表、作者列表
2. 点击作者,会列出其出版的图书列表
3. 点击出版社,会列出旗下图书列表
4. 可以创建、修改、删除 图书、作者、出版社

升级需求:10%
1. 点击修改按钮,弹出模块框,模态框中展示该书的信息且信息可以修改,
2. 书名不可重复,不可修改
3. 修改图书信息时,使用ajax请求发送信息

  

三,编码规范需求:

编码规范需求:15%
1. 代码规范遵守pep8 (https://python.org/dev/peps/pep-0008/)
2. 函数有相应的注释
3. 程序有文档说明文件(README.md参考:https://github.com/csrftoken/vueDrfDemo)
4. 程序的说明文档必须包含的内容:程序的开发环境(django版本)、程序的实现的功能、程序的启动方式、登录用户信息、程序的运行效果
5. 程序设计的流程图:
(可以参考:https://www.processon.com/view/link/589eb841e4b0999184934329)

  

四,项目思路

   首先做这个项目之前,推荐看一下我之前的博客:Django学习笔记(7):单表操作和多表操作,甚至有必要练习一遍。

  其次,前期的基本操作,我在这里不再累赘,如果不会的话,可以参考我之前的博客:Django学习笔记(10)——Book单表的增删改查页面,当然,要是单表操作的增删改查都不会,我觉得先练习一遍没有坏处。

  最后这里写一下此项目的思路。此项目就是对多表操作的再巩固。

4.1 创建模型

  我们先来设定概念,字段和关系

作者模型            :   姓名  年龄
 
 
作者详细模型      :   生日   手机号码   家庭住址等等
 
 
出版商模型          :   出版商名称   所在城市   email
 
 
书籍模型            :   书名  出版日期
 

用户信息模型       :用户名  密码
(登录注册的用户信息)


 
(作者模型  和 作者详细模型     一对一关系        one - to - one)

(一本书可能会有多个作者, 一个作者也可以写多本书  )
(   作者  和 书籍                    多对多的关系  many - to - many)

(一本书只应该由一个出版商出版) 
(    出版商和书籍                  一对多关联关系  one - to  - many)

  代码如下:

from django.db import models

# Create your models here.

# 出版社类
class Publish(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)

    def __str__(self):
        return self.name

# 书类
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    price = models.DecimalField(max_digits=8, decimal_places=2)

    # 书只能关联一个出版社,外键通常建立在多的那一边
    publish = models.ForeignKey(to=''Publish'', on_delete=models.CASCADE)

  # 多对多,作者和书是多对多的关系
    books = models.ManyToManyField(to=''Book'', related_name=''authors'')
    
    def __str__(self):
        return self.title

# 作者类
class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    # 作者和作者详细信息 是一对一的关系
    authordetail = models.OneToOneField(to=''AuthorDetail'', null=True, 
on_delete=models.CASCADE)


# 作者详细信息
class AuthorDetail(models.Model):
    nid = models.AutoField(primary_key=True)
    birthday = models.DateField()
    telephone = models.BigIntegerField()
    addr = models.CharField(max_length=64)

  

  最后运行下面代码,生成数据表:

python manage.py  makemigrations


python manage.py migrate

  我们可以添加数据,我添加的数据如下:

insert into user1_authordetail values(1, ''2019-11-12'', ''12345'', ''boston'');
insert into user1_authordetail values(2, ''2014-11-12'', ''12312'', ''houston'');
insert into user1_authordetail values(3, ''2013-11-12'', ''12432'', ''ripcity'');

insert into user1_author values(1, ''james'', 34, 1);
insert into user1_author values(2, ''durant'', 31, 2);
insert into user1_author values(3, ''curry'', 30, 3);

insert into user1_publish values(1, ''机械出版社'');
insert into user1_publish values(2, ''草堂出版社'');
insert into user1_publish values(3, ''人民出版社'');

  

4.2 APP下的URL具体内容整理

1,注册
    /register/

2,登录
    /login/

3,注销
    /logout/

4,图书列表
    /book_list/
    /add_book/
    /update_book/101/
    /del_book/

5,出版社列表
    /publisher_list/
    /add_publisher/
    /update_publisher/101/
    # 通过出版社,修改该书,之后返回出版社页面
    /update_book/101/202/publisher/
    /del_publisher/
    # 通过出版社,查看该出版社的图书列表
    /book_list/101/publisher/
    # 通过出版社,增加该出版社的某本书
    /add_book/101/publisher/
    # 通过出版社,删除该出版社的某本书
    /del_book/101/publisher/

6,作者列表
    /author_list/
    /add_author/
    /update_author/101/
    #通过作者,修改该书,之后返回作者页面
    /update_book/101/21/author
    /del_author
    # 通过作者,查看该作者的图书列表
    /book_list/101/author/
    # 通过作者,增加该作者的某本书
    /add_book/101/author/
    # 通过作者,删除该作者的某本书
    /del_book/101/author/

  这里我们以book单表的增删改查页面为例,然后增加连表操作,因为作者模型和作者详细模型是一对一关系,而且书籍和作者是多对多的关系。我们这里以书籍,作者,作者详细信息和出版社四张表的关系为例。

4.3  多表的查看操作

  查看操作比较简单,首先在view视图函数中获取所有book数据,将其显示在页面中,代码如下:

book_list = Book.objects.all()

  通过上面代码获取数据,拿到数据后,渲染给前端,前端通过for循环的方式,取出数据。目的就是通过Book这个数据表里面的字段拿到对应的数据。

  前端代码中,我们使用Bootstrap加入简单的样式,然后通过for循环拿出数据,将其显示在前端页面上。

  注意1:这里Book表中,拿出的publishDate数据我们可以改成自己想要显示的模型。

  这里我们可以使用变量的过滤器(filter)。将数据显示成 “xxxx-xx-xx”,代码如下:

{{ book.publishDate|date:''Y-m-d'' }}

  注意2:这里的Book表中,因为一本书可能对应多个作者,所以我们在前端显示的时候,想要给每个作者后面添加一个逗号(,),好看一下,但是最后一个不显示逗号(,)。我们可以使用一个for循环,将其循环显示:

{% for author in book.authors.all %}
       {% if forloop.last %}
             <span>{{ author.name }}</span>
        {% else %}
               <span>{{ author.name }}</span>,
        {% endif %}
{% endfor %}

  其他就没什么难点,这里将books的HTML代码显示如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7/dist/css/bootstrap.min.css">
</head>
<body>
<h3>查看书籍</h3>

<div>
    <div>
        <div>
            <a href="">添加页面</a>
           <table>
               <thead>
                    <tr>
                        <th>编号</th>
                        <th>书籍名称</th>
                        <th>价格</th>
                        <th>出版日期</th>
                        <th>出版社</th>
                        <th>作者</th>
                        <th>修改操作</th>
                        <th>删除操作</th>
                    </tr>
               </thead>
               <tbody>
                    {% for book in book_list %}
                    <tr>
                        <td>{{ forloop.counter }}</td>
                        <td>{{ book.title }}</td>
                        <td>{{ book.price }}</td>
                        <td>{{ book.publishDate|date:''Y-m-d'' }}</td>
                        <td>{{ book.publish.name }}</td>
                        <td>{% for author in book.authors.all %}
                            {% if forloop.last %}
                                <span>{{ author.name }}</span>
                            {% else %}
                            <span>{{ author.name }}</span>,
                            {% endif %}
                            {% endfor %}
                        </td>
                        
                    </tr>
                    {% endfor %}
               </tbody>
           </table>
        </div>
    </div>
</div>
</body>
</html>

  

4.4  多表的增加操作

   添加操作的话,我们可以从前端写起,我们获取数据的各个变量,将其写在form表单里面,然后传到后台,如果是post的数据,我们可以将其获取到,然后添加到数据库中。

  但是首先,我们需要获取出版社的信息和作者的信息,因为出版社和Book是一对多的关系,作者和Book是多对多的关系,所以这里不是单纯的添加,而是需要选择。获取Publish和Author信息的代码如下:

publish_list = Publish.objects.all()

    author_list = Author.objects.all()

  然后,将信息展示在前端,从前端获取的数据通过post传到后台,进而写入数据库。

if request.method == ''POST'':
        title = request.POST.get(''title'')
        price = request.POST.get(''price'')
        pub_date = request.POST.get(''pub_date'')
        publish_id = request.POST.get(''publish_id'')
        # checkbox  多选的话,取值方式如下
        authors_id_list = request.POST.getlist(''authors_id_list'')

        # 添加到数据库中,前面是数据库的名字 =后面是上面变量的名字
        book_obj = Book.objects.create(title=title, price=price, publishDate=pub_date,
                            publish_id=publish_id)

        book_obj.authors.add(*authors_id_list)
        return HttpResponse("OK")

  注意1:由于一本书可以对应多个作者,所以我们获取的作者数据可能是多个,那么我们不能直接get数据,这里使用 gstlist获取多选数据。

authors_id_list = request.POST.getlist(''authors_id_list'')

  注意2:由于作者和书是多对多的关系,所以我们添加数据,不止给Book表里添加,还将会添加Book和Authors表的关系,这里使用.add() 添加。代码如下:

book_obj.authors.add(*authors_id_list)

  最后这里展示一下添加数据的HTML代码,(同样这里使用Bootstrap渲染了页面):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7/dist/css/bootstrap.css">

</head>
<body>
<h3>添加书籍</h3>

<div>
    <div>
        <div>
            <form action="" method="post">
            {% csrf_token %}
                <div>
                    <label for="">名称</label>
                    <input type="text" name="title">
                </div>
                <div>
                    <label for="">价格</label>
                    <input type="text" name="price">
                </div>
                <div>
                    <label for="">出版日期</label>
                    <input type="date" name="pub_date">
                </div>
                <div>
                    <label for="">出版社</label>
                    <select name="publish_id"  id="">
                        {% for publish in publish_list %}
                        <option value="{{ publish.pk }}">{{ publish.name }}</option>
                        {% endfor %}
                    </select>
                </div>
                <div>
                    <label for="">作者</label>
                    <select  name="authors_id_list" multiple>
                        {% for author in author_list %}
                        <option value="{{ author.pk }}">{{ author.name }}</option>
                        {% endfor %}
                    </select>
                </div>
                <input type="submit">
            </form>
        </div>
    </div>
</div>
</body>
</html>

  

4.5  多表的编辑操作和删除操作

  这里将编辑操作和删除操作放在一起写,肯定有其道理的。因为编辑操作和删除操作,只需要在查看书籍的HTML上添加两个按钮,也就是编辑按钮和删除按钮。

<table>
               <thead>
                    <tr>
                        
                            ......
    
                        <th>修改操作</th>
                        <th>删除操作</th>
                    </tr>
               </thead>
               <tbody>
                    {% for book in book_list %}
                    <tr> 

                       ......

                        <td>
                            <a href="{{ book.pk }}/change">编辑</a>
                        </td>
                        <td>
                            <a href="{{ book.pk }}/delete">删除</a>
                        </td>
                    </tr>
                    {% endfor %}
               </tbody>
           </table>

  

4.5.1 编辑功能的制作

  首先,我们需要拿到需要编辑的ID,然后在数据库中找到对应的ID字段,最后对内容进行修改。

edit_book_obj = Book.objects.filter(pk=edit_id).first()
    publish_list = Publish.objects.all()
    author_list = Author.objects.all()

  这里再重复一次,因为Book和Publish是一对多的关系,Book和author是多对多的关系,所以同时需要将publish和author的数据取出来,同时进行update。

def change_books(request,edit_id):
    edit_book_obj = Book.objects.filter(pk=edit_id).first()
    publish_list = Publish.objects.all()
    author_list = Author.objects.all()

    if request.method == ''POST'':
        title = request.POST.get(''title'')
        price = request.POST.get(''price'')
        pub_date = request.POST.get(''pub_date'')
        publish_id = request.POST.get(''publish_id'')
        # checkbox  多选的话,取值方式如下
        authors_id_list = request.POST.getlist(''authors_id_list'')

        # 添加到数据库中,前面是数据库的名字 =后面是上面变量的名字
        book_obj = Book.objects.filter(pk=edit_id).update(title=title, price=price, publishDate=pub_date,
                                       publish_id=publish_id)
        # 先把更新过的数据清空掉,然后再添加信息
        # edit_book_obj.authors.clear()
        # edit_book_obj.authors.add(*authors_id_list)
        # set方法 可以取代上面的两行代码。
        edit_book_obj.authors.set(authors_id_list)

        return redirect(''/user1/books/'')

    return render(request, ''user1/editbook.html'', locals())

  除了之前需要注意的问题之外,这里我们还需要注意两点。

  注意1:我们将数据update之后,我们需要展示的编辑之后的Book页面,所以这里使用redirect,返回到book表中:

return redirect(''/user1/books/'')

  注意2:我们添加author表的数据,所以我们不能拿到更新之后的信息,这里需要更新之前的信息,所以我们需要把更新过的数据清空掉,然后再添加数据。这里有两种方法:

# 先把更新过的数据清空掉,然后再添加信息

#  方法一
        edit_book_obj.authors.clear()
        edit_book_obj.authors.add(*authors_id_list)

# 方法二
        # set方法 可以取代上面的两行代码。
        edit_book_obj.authors.set(authors_id_list)

  最后展示完整的编辑视图函数:

def change_books(request,edit_id):
    edit_book_obj = Book.objects.filter(pk=edit_id).first()
    publish_list = Publish.objects.all()
    author_list = Author.objects.all()

    if request.method == ''POST'':
        title = request.POST.get(''title'')
        price = request.POST.get(''price'')
        pub_date = request.POST.get(''pub_date'')
        publish_id = request.POST.get(''publish_id'')
        # checkbox  多选的话,取值方式如下
        authors_id_list = request.POST.getlist(''authors_id_list'')

        # 添加到数据库中,前面是数据库的名字 =后面是上面变量的名字
        book_obj = Book.objects.filter(pk=edit_id).update(title=title, price=price, 
publishDate=pub_date,
                                       publish_id=publish_id)
        # 先把更新过的数据清空掉,然后再添加信息
        # edit_book_obj.authors.clear()
        # edit_book_obj.authors.add(*authors_id_list)
        # set方法 可以取代上面的两行代码。
        edit_book_obj.authors.set(authors_id_list)

        return redirect(''/user1/books/'')

    return render(request, ''user1/editbook.html'', locals())

  

4.5.2 删除功能的制作

  删除就非常简单了,我们只需要通过查找需要编译的ID,然后在数据库找到对应ID字段,删除ID字段对应的数据即可。最后返回Book表就行。

  这里展示删除视图的完整代码:

def delete_books(request,delete_id):
    Book.objects.filter(pk=delete_id).delete()

    return redirect(''/user1/books/'')

  

五,注意事项

 1,ORM表关系

一对一(author authordetail)

  删除author表时,应该删除authordetail表,关联的author就被删除了!

一对多(book  publisher)

  删除出版社下面的某本书,拿到书的id,删除这本书

多对多(book author)

  清楚绑定关系,不是删除书

1.1 打印ORM转换过程中SQL的代码

  如果不了解其关系,可以在控制台打印其SQL语句,也就是打印ORM转换过程中的SQL,我们可以在Settings中配置一下:

LOGGING = {
    ''version'': 1,
    ''disable_existing_loggers'': False,
    ''handlers'': {
        ''console'':{
            ''level'':''DEBUG'',
            ''class'':''logging.StreamHandler'',
        },
    },
    ''loggers'': {
        ''django.db.backends'': {
            ''handlers'': [''console''],
            ''propagate'': True,
            ''level'':''DEBUG'',
        },
    }
}

  

1.2  related_name的应用

  在反向查询的时候,如果定义了related_name,则使用 related_name替换表名,例如:

publish = ForeignKey(Blog, related_name=''bookList'')

  练习:

# 练习: 查询人民出版社出版过的所有书籍的名字与价格(一对多)
 
# 反向查询 不再按表名:book,而是related_name:bookList
 
 
    queryResult=Publish.objects
              .filter(name="人民出版社")
              .values_list("bookList__title","bookList__price")

  

2,引入Bootstrap文件

2.1 配置搜索目录

  在项目的根目录下新建一个static目录,然后打开项目的settings文件,在最下面添加配置,用于指定静态文件的搜索目录:

STATIC_URL = ''/static/''

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, ''static''),
]

  

2.2 下载Bootstrap文件

  找到Bootstrap官网:点击我

  然后下载Bootstrap,将其解压,然后放到static下面即可。

 2.3 使用Bootstrap文件

  注意:Bootstrap的所有JavaScript插件都依赖jQuery,所以必须将jQuery放在前面,不然bootstrap永远引入失败。

<!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
<script src="/static/JS/jquery-3.2.1.min.js"></script>

<!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
<script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script>

  

3,locals()函数的使用技巧

  对于添加函数,我们发现了其return的时候,赋值操作如下:

return render(request, ''user1/addbook.html'', {''publish_list'': publish_list,
 ''author_list'': author_list})

  我们会发现,如果临时变量和临时模板有很多的时候,输起来就非常麻烦,所以为了代码看起来更加简洁,我们可以使用Python的内建函数 locals(),它返回的字典对所有局部变量的名称和值进行映射。因此上面的代码可以重构如下:

return render(request, ''user1/addbook.html'', locals())

  在上面的代码中,我们没有手工指定其字典形式,而是传入了locals()的值,他包括了函数执行的一切变量。

 

六,笔记

1,Django中timezone和Python中datatime的区别与联系

  • 进入django数据库中的时候一定是timezone aware的时间,如果要生成时间,要通过replace timezone设置为本地失去。
  • 数据库中的时间都是UTC时间  

1.1  Django中的timezone

1.2  Python中的datetime

 2,def  __str__(self): 是什么意思?

  Python3.x中使用的是__str__ 函数 ,而Python2.x中使用的是__unicode__函数。

  推荐定义Model的时候,写一个__str__函数,它的作用是美化打印出来的结果,使我们更方便查看。

  从最基本的说起,首先举个例子:

class Test1:
    def __init__(self, name, age):
        self.name = name
        self.age = age

class Test2:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return "Name: " + self.name

res1 = Test1(''james'', 32)
print(res1)
res2 = Test2(''durant'', 30)
print(res2)
''''''
<__main__.Test1 object at 0x00000000025700B8>
Name: durant
''''''

  我们可以看到:如果没有__str__方法,打印出来的是<__main__.Test1 object at 0x00000000025700B8>格式,而有__str_-方法,打印出来是按照__str__定义的格式来打印,打印结果是Name: durant。

  看看文档

   那么具体到Django中,在Models中定义这个__str__方法,最直接的感受就是你访问admin所看到的的内容是否友好。

  下面举个例子:

class Question(models.Model):
    question_text = models.CharField(''question text'', max_length=200)
    pub_date = models.DateTimeField(''date published'')

    class Meta:
        verbose_name = ''question''
        verbose_name_plural = verbose_name
        ordering = [''-id'']

    def __str__(self):
        return self.question_text

  上面的models来自Django的官方教程(本人没有实践,只是拿过来做个笔记而已),在上面的models中,我定义了一个名为Question的models,此时访问Django的后台admin,看到的内容是这样的:

  可以发现,Question看到的内容就是我们上面返回的self.question_text(如果返回的是其他形式,则结果是其他形式),那么如果此时将上述代码注释掉,也就是变成下面这样:

class Question(models.Model):
    question_text = models.CharField(''question text'', max_length=200)
    pub_date = models.DateTimeField(''date published'')

    class Meta:
        verbose_name = ''question''
        verbose_name_plural = verbose_name
        ordering = [''-id'']

  再去看看后台admin里面查看Question,则变成这样:

  我们定义的问题,看到的内容全部变成了Question object,这样的表达方式,对人来说,是相当的不友好,所以这就是定义一个__str__函数的必要性。

 

 3,使用类视图:减少重复代码

   当视图里面代码相似的时候,就显得有点冗余。因为他们都具有类似的业务逻辑,实现类似的功能:通过从URL传递过来的参数去数据库查询数据,加载一个模板,利用刚才的数据渲染模板,返回这个模板,由于这个过程是如此的常见,Django很善解人意的帮助我们想办法偷懒,于是提供了一种快捷方式,名为“类视图”。

  现在我们可以试试将原来代码改为使用类视图的方式,整个过程分三步走:

  • 修改URLconf设置
  • 删除一些旧的无用的视图
  • 采用基于类视图的新视图

 3.1 改良路由配置系统URLconf

  URL配置(URLConf)就像是Django所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表;你就是以这种方式告诉Django,对于客户端发来的某个URL调用哪一段逻辑代码对应执行。

  比如下面代码:

from django.conf.urls import url
from . import views
 
app_name = ''polls''
urlpatterns = [
    url(r''^$'', views.IndexView.as_view(), name=''index''),
    url(r''^(?P<pk>[0-9]+)/$'', views.DetailView.as_view(), name=''detail''),
    url(r''^(?P<pk>[0-9]+)/results/$'', views.ResultsView.as_view(), name=''results''),
    url(r''^(?P<question_id>[0-9]+)/vote/$'', views.vote, name=''vote''),
]

  请注意:在上面的第2,3条目中将原来的question_id 改成了pk。若要从URL中捕获一个值,只需要在它的周围放置一对圆括号,不需要添加一个前导的反斜杠,因为每个URL都有。比如应该是^articles 而不是 ^/articles。  

  注意:每个正则表达式前面的 r 是可选的额但是建议加上,他告诉Python这个字符串是“原始的”——字符串中任何字符都不应该转义。

 

七,结果展示

1,数据库建好的表展示

1.1 插入数据之前,表格展示如下:

  此次项目创建的所有的表格展示如下:

  author表

  author_books表

  

   authordetail表

  book表

  publisher表

  userinfo表

 

2,前端页面展示如下:

Book表:

编辑页面:

添加页面:

 

八,代码

8.1 简易版本的增伤改查Book表

models.py

from django.db import models

# Create your models here.

# 出版社类
class Publish(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)

    def __str__(self):
        return self.name

# 书类
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    price = models.DecimalField(max_digits=8, decimal_places=2)

    # 书只能关联一个出版社,外键通常建立在多的那一边
    publish = models.ForeignKey(to=''Publish'', on_delete=models.CASCADE)

    def __str__(self):
        return self.title

# 作者类
class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    # 作者和作者详细信息 是一对一的关系
    authordetail = models.OneToOneField(to=''AuthorDetail'', null=True, on_delete=models.CASCADE)

    # 多对多,作者和书是多对多的关系
    books = models.ManyToManyField(to=''Book'', related_name=''authors'')

# 作者详细信息
class AuthorDetail(models.Model):
    nid = models.AutoField(primary_key=True)
    birthday = models.DateField()
    telephone = models.BigIntegerField()
    addr = models.CharField(max_length=64)

  

views.py

from django.shortcuts import render, HttpResponse,redirect

# Create your views here.
from user1.models import *

def add_book(request):
    if request.method == ''POST'':
        title = request.POST.get(''title'')
        price = request.POST.get(''price'')
        pub_date = request.POST.get(''pub_date'')
        publish_id = request.POST.get(''publish_id'')
        # checkbox  多选的话,取值方式如下
        authors_id_list = request.POST.getlist(''authors_id_list'')

        # 添加到数据库中,前面是数据库的名字 =后面是上面变量的名字
        book_obj = Book.objects.create(title=title, price=price, publishDate=pub_date,
                            publish_id=publish_id)
        # print(authors_id_list)   #[''2'', ''3'']
        book_obj.authors.add(*authors_id_list)
        return HttpResponse("OK")

    publish_list = Publish.objects.all()
    print(publish_list)
    author_list = Author.objects.all()
    return render(request, ''user1/addbook.html'', {''publish_list'': publish_list, ''author_list'': author_list})

def books(request):
    book_list = Book.objects.all()

    return render(request, ''user1/books.html'',{''book_list'': book_list})

def change_books(request,edit_id):
    edit_book_obj = Book.objects.filter(pk=edit_id).first()
    publish_list = Publish.objects.all()
    author_list = Author.objects.all()

    if request.method == ''POST'':
        title = request.POST.get(''title'')
        price = request.POST.get(''price'')
        pub_date = request.POST.get(''pub_date'')
        publish_id = request.POST.get(''publish_id'')
        # checkbox  多选的话,取值方式如下
        authors_id_list = request.POST.getlist(''authors_id_list'')

        # 添加到数据库中,前面是数据库的名字 =后面是上面变量的名字
        book_obj = Book.objects.filter(pk=edit_id).update(title=title, price=price, publishDate=pub_date,
                                       publish_id=publish_id)
        # 先把更新过的数据清空掉,然后再添加信息
        # edit_book_obj.authors.clear()
        # edit_book_obj.authors.add(*authors_id_list)
        # set方法 可以取代上面的两行代码。
        edit_book_obj.authors.set(authors_id_list)

        return redirect(''/user1/books/'')

    return render(request, ''user1/editbook.html'', {''edit_book_obj'': edit_book_obj,
                                                   ''publish_list'': publish_list,
                                                   ''author_list'': author_list})

def delete_books(request,delete_id):
    Book.objects.filter(pk=delete_id).delete()

    return redirect(''/user1/books/'')

  

urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path(''admin/'', admin.site.urls),
    path(''user1/'', include(''user1.urls'')),
]

  

user1/urls.py

from django.contrib import admin
from django.urls import path, include

from user1 import views

urlpatterns = [
    path(''add/'', views.add_book),
    path(''books/'', views.books),
    path(''books/<int:edit_id>/change'', views.change_books),
    path(''books/<int:delete_id>/delete'', views.delete_books),
]

  

 

8.2 完整项目的代码

  请移步小编的GitHub:传送门

 

原文出处:https://www.cnblogs.com/wj-1314/p/10341310.html

Django学习笔记(17)——BBS+Blog项目开发(1)验证码功能的实现

Django学习笔记(17)——BBS+Blog项目开发(1)验证码功能的实现

  本文主要学习验证码功能的实现,为了项目BBS+Blog项目打下基础。

   为了防止机器人频繁登陆网站或者破坏分子恶意登陆,很多用户登录和注册系统都提供了图形验证码功能。

  验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试。

  图形验证码的历史比较悠久,到现在已经有点英雄末路的味道了。因为机器学习、图像识别的存在,机器人已经可以比较正确的识别图像内的字符了。但不管怎么说,作为一种防御手段,至少还是可以抵挡一些低级入门的攻击手段,抬高了攻击者的门槛。

验证码功能实现方法1——完整的验证码实现流程

1, 验证码前端画布页面生成

  那么下面我们将依次生成验证码这种画布,左边框是输入验证码的内容,右边框是设计验证码。

1.1,直接读取图片

  首先,我们需要了解读取图片的方法。

# 方式一
    with open(''kd1.jpg'', ''rb'') as f:
        data = f.read()

    return HttpResponse(data)

  这样我们知道方法就行,不建议使用,因为这样读取文件,我们需要存很多文件。

1.2,硬盘操作

  首先,我们需要导入库,图像处理库Pillow,如果没有的话,需要先安装pillow库。

  其次,我们创建一个定义随机颜色方法的函数。这函数的意义就是定义画布的背景颜色。

如何定义颜色随机呢?,因为三原色R,G,B的范围都是0-255,我们随机取0-255里面的某一个整数,就得到了随机颜色。代码如下:

#定义随机颜色方法
def get_random_color():
    
    R = random.randrange(255)
    G = random.randrange(255)
    B = random.randrange(255)

    return (R,G,B)

  然后我们设定画布的大小,定义画布的宽和高。然后写一块区域,最后展示在前端页面。

# 方式二:使用pillow PIL模块
    # pip  install pillow
    from PIL import Image

    # img = Image.new("RGB", (270, 40), color=''red'')
    img = Image.new("RGB", (270, 40), color=get_random_color())
    with open("validCode.png", "wb") as f:
        img.save(f, ''png'')
    with open(''validCode.png'', ''rb'') as f:
        data = f.read()
    return HttpResponse(data)

  

1.3,内存操作

  其实硬盘操作,我们也不建议使用,最好的方法是建议使用内存操作。在上线中大多体验者需要的是速度,如果速度不够快,那就会放弃这个APP,所以我们一般牺牲其空间复杂度,换取时间复杂度。

  那内存操作如何做呢?首先,我们获取一个缓存区,然后将图片保存到缓存区,最后使用一个.getvalue的函数方法将缓存区的内容返回给前端。

  .getvalue  是把缓存区的所有数据读取。

  bytesIO() 是使用io 获取一个缓存区,然后将图片保存到缓存区,最后读取缓存区的数据。

from PIL import Image, ImageDraw, ImageFont
    from io import BytesIO

    img = Image.new(''RGB'', (270, 40), color=get_random_color())

    f = BytesIO()
    img.save(f, ''png'')
    data = f.getvalue()

  这样的话,一个前端画布就生成了,不仅生成了,我们还解决了速度问题,而且每次刷新,后台都会传出不同颜色的画布。

1.4 效果图

  验证码在前端显示,肯定是左边一格,右边一格。最后设计出来的画布效果是这样的。

 

  那下面,我们需要给画布里面添加文字了。

2,如何给面板里加文字

  我们需要导入绘图库 ImageDraw, 绘图字体库 ImageFont。还需要下载一个字体样式,直接使用网上的字体样式会比较简单。

2.1,ttf字体下载

  我们可以在网上下载一个ttf字体文件。选择一个自己喜欢的字体形式,下载。我这里下载了三个,这里我使用第三个 Vera.ttf字体。

 2.2 添加文字

   一般情况下,验证码都是由四到八位不等的数字,大小写字母组成。我们这里选择五位,其中每个位里面让随机生成一个大写字母,或者小写字母,或者数字。然后将其保存下来。这就是一个简单的验证码就生成了。

  代码如下:

# 导入绘图库
    from PIL import Image, ImageDraw, ImageFont
    # 导入IO库
    from io import BytesIO

    img = Image.new(''RGB'', (270, 40), color=get_random_color())
    draw = ImageDraw.Draw(img)
    vera_font = ImageFont.truetype("static/font/Vera.ttf", size=35)


    #  ttf 字体下载
    valid_code_str = ''''
    for i in range(5):
        # 数字
        random_num = str(random.randint(0, 9))
        # 随机小写字母
        random_low_alpha = chr(random.randint(95, 122))
        # 随机大写字母
        random_upper_alpha = chr(random.randint(65, 90))

        random_char = random.choice([random_num, random_upper_alpha, random_low_alpha])
        #  x, y  坐标
        draw.text((i*50+20, 5), random_char, get_random_color(), font=vera_font)
        # 保存验证码字符串
        valid_code_str += random_char

  

2.3 效果图

  验证码里面添加五个文字,最后设计出来的添加文字的画布效果是这样的。

 

3,加上噪点噪线

  其实上面差不多已经完成了验证码的操作,但是这是比较简单的验证码,一般的图片验证码机器就可以识别了。我们需要添加一些难度,比如噪点噪线。

  加噪点噪线也是为了减轻暴力请求,一般的图片验证码机器就可以识别出来,所以我们加点难度,这样可以减轻服务器的压力。

  下面代码中,width和height是前端画布的宽和高,第一个for循环是噪点的加入,第二个for循环是噪线的加入。

width = 235
    height = 35
    for i in range(100):
        x1 = random.randint(0, width)
        x2 = random.randint(0, width)
        y1 = random.randint(0, height)
        y2 = random.randint(0, height)
        draw.line((x1, x2, y1, y2), fill=get_random_color())

    for i in range(400):
        draw.point([random.randint(0, width), random.randint(0, height)],
                   fill=get_random_color())
        x = random.randint(0, width)
        y = random.randint(0, height)
        draw.arc((x, y, x+4, y+4), 0, 90, fill=get_random_color())

  x1, y1, x2, y2  则表示需要四个坐标。

 3.1 效果图

    验证码里面添加噪点噪线,因为我还要使用,所以噪点噪线的效果就添加了一点点而已,如果需要复杂,可以添加内容就像,最后设计出来的添加噪点噪线的画布效果是这样的。

 

4,验证码局部刷新

  为什么要做验证码局部刷新呢?从使用者的角度来说,我们也知道,当前端显示的验证码看不清楚,我们就需要刷新一下,但是不可能不停的提交表单,这样的话,服务器的压力就太大了。所以这时候局部刷新就优势就显现出来了,我们需要局部刷新,很多人就想到了Ajax,没错,这是可以用,但是这里说一种比较简单的局部刷新的方法。

  我们可以看一个例子:

   从上面,我们发现可以使用增加字符的方式,实现局部刷新,那么我们也可以采取这么简单的方法,直接刷新验证码就行了。

  下面展示其代码:

<script src="/static/JS/jquery-3.2.1.js"></script>
<script>
    //刷新验证码
    $("#valid_code_img").click(function () {
        $(this)[0].src += "?"
    })

</script>

  views.py

def get_validCode_image(request):
    def get_random_color():
        return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

    # 方式四,图片里面什么都没,这里我们需要加上噪点,文字
    from PIL import Image, ImageDraw, ImageFont
    from io import BytesIO

    img = Image.new(''RGB'', (270, 40), color=get_random_color())
    draw = ImageDraw.Draw(img)
    vera_font = ImageFont.truetype("static/font/Vera.ttf", size=35)

    valid_code_str = ''''
    for i in range(5):
        # 数字
        random_num = str(random.randint(0, 9))
        # 随机小写字母
        random_low_alpha = chr(random.randint(95, 122))
        # 随机大写字母
        random_upper_alpha = chr(random.randint(65, 90))

        random_char = random.choice([random_num, random_upper_alpha, random_low_alpha])
        #  x, y  坐标
        draw.text((i*50+20, 5), random_char, get_random_color(), font=vera_font)
        # 保存验证码字符串
        valid_code_str += random_char

    width = 235
    height = 35
    for i in range(10):
        x1 = random.randint(0, width)
        x2 = random.randint(0, width)
        y1 = random.randint(0, height)
        y2 = random.randint(0, height)
        draw.line((x1, x2, y1, y2), fill=get_random_color())

    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)],
                   fill=get_random_color())
        x = random.randint(0, width)
        y = random.randint(0, height)
        draw.arc((x, y, x+4, y+4), 0, 90, fill=get_random_color())

    print(''valid_code_str'', valid_code_str)

    f = BytesIO()
    img.save(f, ''png'')
    data = f.getvalue()

    request.session[''valid_code_str''] = valid_code_str

    return HttpResponse(data)

  前端代码:

// 登录验证
    $(".login_btn").click(function () {
        $.ajax({
            url: "",
            type: ''post'',
            data: {
                user: $(''#user'').val(),
                pwd: $(''#pwd'').val(),
                valid_code: $("#valid_code").val(),
                csrfmiddlewaretoken: $("[name=''csrfmiddlewaretoken'']").val(),
            },
            success: function (data) {
                console.log(data);
                if (data.user){
                        if (location.search){
                            location.href = location.search.slice(6)
                        }
                        else {
                            location.href = ''/blog/index/''
                        }
                }else {
                    $(".error").text(data.msg).css({''color'':''red'', ''margin-left'':''10px''});
                    //  设置2秒后清空
                    setTimeout(function () {
                        $(".error").text("")
                    }, 2000)
                }
            }
        })
    });

  

5,cookie与session保存验证码

   将随机验证码存储到session中,就是需要记录给哪个请求发了什么验证码。

  为什么这么说呢?因为到时候使用登录验证的使用者不止一个人,那么服务端发送的验证码不记录给哪个请求发送的验证码,当好几个人同时请求验证码信息,然后提交,这样就会发生混淆。所以保险起见,我们使用session保存验证码。

    views.py

request.session[''valid_code_str''] = valid_code_str

  login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <link rel="stylesheet" href="/static/blog/bootstrap-3.3.7-dist/css/bootstrap.css">

</head>
<body>
<h3>登录页面</h3>
<div>
    <div>
        <div>
            <form>
                {% csrf_token %}
                <div>
                    <label for="user">用户名</label>
                    <input type="text" id="user">
                </div>
                <div>
                    <label for="pwd">密码</label>
                    <input type="password" id="pwd">
                </div>

                <div>
                    <label for="pwd">验证码</label>
                    <div>
                        <div>
                            <input type="text" id="valid_code">
                        </div>
                        <div>
                            <img width="235" height="35" id="valid_code_img" src="/blog/get_validCode_image/" alt="">
                        </div>
                    </div>
                </div>

{#                <input type="submit"value="submit">#}
                <input type="button"value="submit"><span></span>
{#                <input type="button"value="submit">#}
            </form>
        </div>
    </div>
</div>

<script src="/static/JS/jquery-3.2.1.js"></script>
<script>
    //刷新验证码
    $("#valid_code_img").click(function () {
        $(this)[0].src += "?"
    });

    // 登录验证
    $(".login_btn").click(function () {
        $.ajax({
            url: "",
            type: ''post'',
            data: {
                user: $(''#user'').val(),
                pwd: $(''#pwd'').val(),
                valid_code: $("#valid_code").val(),
                csrfmiddlewaretoken: $("[name=''csrfmiddlewaretoken'']").val(),
            },
            success: function (data) {
                console.log(data);
                if (data.user){
                        if (location.search){
                            location.href = location.search.slice(6)
                        }
                        else {
                            location.href = ''/blog/index/''
                        }
                }else {
                    $(".error").text(data.msg).css({''color'':''red'', ''margin-left'':''10px''});
                    //  设置2秒后清空
                    setTimeout(function () {
                        $(".error").text("")
                    }, 2000)
                }
            }
        })
    });


</script>
</body>


</html>

  index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Index Page</h1>
<p>{{ request.user.username }}</p>
</body>
</html>

  views.py

from django.shortcuts import render, HttpResponse, redirect

# Create your views here.
# from .MyForms import UserTestForm
# from .models import UserTest
from django.http import JsonResponse
from django.contrib import auth
import random


def login(request):
    # if request.method == ''POST'':
    #     response = {''user'': None, ''msg'': None}
    #
    #     user = request.POST.get(''user'')
    #     pwd = request.POST.get(''pwd'')
    #     valid_code = request.POST.get(''valid_code'')
    #     valid_code_str = request.session.get(''valid_code_str'')
    #     # if valid_code == valid_code_str:
    #     # 验证码不区分大小写
    #     if valid_code.upper() == valid_code_str:
    #         pass
    #     else:
    #         # 校验失败了。。。。
    #         response[''msg''] = ''valid code error!''
    #         pass
    #     return JsonResponse(response)
    if request.method == ''POST'':
        print("POST..............")
        response = {''user'': None, ''msg'': None}
        user = request.POST.get(''user'')
        pwd = request.POST.get(''pwd'')
        valid_code = request.POST.get(''valid_code'')

        valid_code_str = request.session.get(''valid_code_str'')
        # if valid_code == valid_code_str:
        # 验证码不区分大小写
        if valid_code.upper() == valid_code_str.upper():
            user = auth.authenticate(username=user, password=pwd)
            if user:
                # request.user == 当前登录对象
                auth.login(request, user)
                response[''user''] = user.username
                pass
            else:
                # response[''msg''] = ''username or password error !''
                response[''msg''] = ''用户名或者密码错误''
        else:
            # 校验失败了。。。。
            response[''msg''] = ''valid code error!''
        return JsonResponse(response)

    return render(request, ''login.html'')

def get_validCode_image(request):
    from blog.utils.validCode import get_valid_code_img
    data = get_valid_code_img(request)
    return HttpResponse(data)




def index(request):
    return render(request, ''index.html'')

  validCode.py

import random


def get_random_color():
    return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

def get_valid_code_img(request):

    # # 方式一
    # with open(''kd1.jpg'', ''rb'') as f:
    #     data = f.read()
    #
    # return HttpResponse(data)

    # # 方式二:使用pillow PIL模块
    # # pip  install pillow
    # from PIL import Image
    #
    # # img = Image.new("RGB", (270, 40), color=''red'')
    # img = Image.new("RGB", (270, 40), color=get_random_color())
    # with open("validCode.png", "wb") as f:
    #     img.save(f, ''png'')
    # with open(''validCode.png'', ''rb'') as f:
    #     data = f.read()
    # return HttpResponse(data)

    # 方式三,因为磁盘的方式比较慢,open是磁盘操作
    # 内存操作会比较快,这里使用内存操作
    # from PIL import Image, ImageDraw, ImageFont
    # from io import BytesIO
    #
    # img = Image.new(''RGB'', (270, 40), color=get_random_color())
    #
    # f = BytesIO()
    # img.save(f, ''png'')
    # data = f.getvalue()


    # 方式四,图片里面什么都没,这里我们需要加上噪点,文字
    from PIL import Image, ImageDraw, ImageFont
    from io import BytesIO

    img = Image.new(''RGB'', (270, 40), color=get_random_color())
    draw = ImageDraw.Draw(img)
    vera_font = ImageFont.truetype("static/font/Vera.ttf", size=35)
    # char = str(random.randint(0, 9))

    # draw.text((0, 5), ''python'', get_random_color(), font=vera_font)
    # draw.text((0, 5), char, get_random_color(), font=vera_font)
    #  ttf 字体下载
    valid_code_str = ''''
    for i in range(5):
        # 数字
        random_num = str(random.randint(0, 9))
        # 随机小写字母
        random_low_alpha = chr(random.randint(95, 122))
        # 随机大写字母
        random_upper_alpha = chr(random.randint(65, 90))

        random_char = random.choice([random_num, random_upper_alpha, random_low_alpha])
        #  x, y  坐标
        draw.text((i*50+20, 5), random_char, get_random_color(), font=vera_font)
        # 保存验证码字符串
        valid_code_str += random_char

    width = 235
    height = 35
    for i in range(10):
        x1 = random.randint(0, width)
        x2 = random.randint(0, width)
        y1 = random.randint(0, height)
        y2 = random.randint(0, height)
        draw.line((x1, x2, y1, y2), fill=get_random_color())

    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)],
                   fill=get_random_color())
        x = random.randint(0, width)
        y = random.randint(0, height)
        draw.arc((x, y, x+4, y+4), 0, 90, fill=get_random_color())

    print(''valid_code_str'', valid_code_str)

    request.session[''valid_code_str''] = valid_code_str
    ''''''
    1, dasdasda
    2, COOKIE {''sessionid'': dsdsadsdad}
    3,django-session 
        session-key  session-data  dsadasdsdsa
    4,
    ''''''
    f = BytesIO()
    img.save(f, ''png'')
    data = f.getvalue()

    return data

  

 

验证码功能实现方法2——captcha

  在Django中实现图片验证码功能非常简单,有现成的第三方库可以使用,我们不必自己开发(但是也需要自己能开发的出来,方法二将自己开发的图片验证码模块)。这个库叫做django-simple-captcha。

1,安装 captcha

  直接使用pip 按照

pip  install django--simple-captcha

  

 

  Django 自动帮我们安装了相关的依赖库 six, olefile 和 pillow ,其中Pillow是大名鼎鼎的绘图模块。

2,注册captcha

  在settings.py中,将''captcha'' 注册到APP列表里。

INSTALLED_APPS = [
    ''django.contrib.admin'',
    ''django.contrib.auth'',
    ''django.contrib.contenttypes'',
    ''django.contrib.sessions'',
    ''django.contrib.messages'',
    ''django.contrib.staticfiles'',
    ''user19'',
    ''captcha'',
]

  captcha 需要在数据库中建立自己的数据表,所以需要执行migrate命名生成数据表:

python manage.py makemigrations

python manage.py migrate

  

3,添加URL路由

  我们还需要在根目录下的urls.py文件添加 captcha 对应的网址:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path(''admin/'', admin.site.urls),
    path(''blog/'', include(''blog.urls'')),
    path(''captcha'', include(''captcha.urls'')),
]

  

4,修改 myforms.py

  如果上面都OK了,就可以直接在MyForms.py文件中添加CaptchaField了。

from django import forms
from captcha.fields import CaptchaField
  
class UserForm(forms.Form):
    username = forms.CharField(label="用户名", max_length=128,
widget=forms.TextInput(attrs={''class'': ''form-control''}))
 
    password = forms.CharField(label="密码", max_length=256,
widget=forms.PasswordInput(attrs={''class'': ''form-control''}))
 
    captcha = CaptchaField(label=''验证码'')

  注意:我们需要提前导入form captcha.fields import CaptchaField ,然后就像写普通的form字段一样添加一个captcha字段就可以了。

5,HTML文件的使用(修改 login.html文件)

  由于我们这里直接学习验证码,所以我这里直接写一个简单的login.html页面,添加 capthca的相关内容。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>

{% block content %}
    <div>
        <div>
          <formform-login'' action="" method="post">
              {% csrf_token %}
              <h2>欢迎登录</h2>
              <div>
                  {{ login_form.username.label_tag }}
                  {{ login_form.username}}
              </div>
              <div>
                  {{ login_form.password.label_tag }}
                  {{ login_form.password }}
              </div>

              <div>
                  {{ login_form.captcha.errors }}
                  {{ login_form.captcha.label_tag }}
                  {{ login_form.captcha }}
              </div>

              <button type="submit">提交</button>

          </form>
        </div>
    </div>
{% endblock %}

</body>
</html>

  这里额外的添加了一条 {{ login_form.captcha.errors }} 用于明确指示用户,你的验证码不正确。其中验证图形码是否正确的工作都是在后台自动完成的。只需要使用is_valid()这个myforms内置的验证方法就一起进行了,完全不需要再视图函数中添加任何的验证代码,非常方便快捷!

6,查看效果

  直接的效果进入页面的效果如下:

  我们输入错验证码的效果如下:

   当然我们还可以添加多种效果,这里就不做添加了,就实现简单的验证码功能即可。

7,附带的代码

  models.py

class UserTest(models.Model):
    ''''''测试验证码,注册的用户表''''''
    name = models.CharField(max_length=128)
    password = models.CharField(max_length=256)

    def __str__(self):
        return self.name

  views.py

def login_test(request):
    if request.method == ''POST'':
        login_form = UserTestForm(request.POST)
        if login_form.is_valid():
            username = login_form.cleaned_data[''username'']
            password = login_form.cleaned_data[''password'']
            try:
                user = UserTest.objects.get(name=username)
                if user.password == password:
                    return HttpResponse("OK")
                else:
                    return HttpResponse("NG")
            except:
                return HttpResponse("NG")
        return render(request, ''login_test.html'', locals())
    login_form = UserTestForm()
    return render(request, ''login_test.html'', locals())

  

 

 

验证码功能实现方法3——极验验证SDK

一,Django极验滑动验证码的使用

1,官网直接注册账号申请key和value

  极验滑动验证码官网:www.geetest.com

2,从Github: gt3-python-sdk下载.zip文件

3,找到里面Django 的文件夹对照官网直接copy

4,代码

  views.py

from geetest import GeetestLib
from django.contrib import auth
from django.http import JsonResponse
from django.shortcuts import render
def login(request):
    if request.method == "POST":
        # 初始化一个给AJAX返回的数据
        ret = {"status": 0, "msg": ""}
        # 从提交过来的数据中 取到用户名和密码
        username = request.POST.get("username")
        pwd = request.POST.get("password")
        # 获取极验 滑动验证码相关的参数
        gt = GeetestLib(pc_geetest_id, pc_geetest_key)
        challenge = request.POST.get(gt.FN_CHALLENGE, '''')
        validate = request.POST.get(gt.FN_VALIDATE, '''')
        seccode = request.POST.get(gt.FN_SECCODE, '''')
        status = request.session[gt.GT_STATUS_SESSION_KEY]
        user_id = request.session["user_id"]

        if status:
            result = gt.success_validate(challenge, validate, seccode, user_id)
        else:
            result = gt.failback_validate(challenge, validate, seccode)
        if result:
            # 验证码正确
            # 利用auth模块做用户名和密码的校验
            user = auth.authenticate(username=username, password=pwd)
            if user:
                # 用户名密码正确
                # 给用户做登录
                auth.login(request, user)  # 将登录用户赋值给 request.user
                ret["msg"] = "/index/"
            else:
                # 用户名密码错误
                ret["status"] = 1
                ret["msg"] = "用户名或密码错误!"
        else:
            ret["status"] = 1
            ret["msg"] = "验证码错误"

        return JsonResponse(ret)
    return render(request, "login.html")
# 请在官网申请ID使用,示例ID不可使用
pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c"
pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4"


# 处理极验 获取验证码的视图
def get_geetest(request):
    user_id = ''test''
    gt = GeetestLib(pc_geetest_id, pc_geetest_key)
    status = gt.pre_process(user_id)
    request.session[gt.GT_STATUS_SESSION_KEY] = status
    request.session["user_id"] = user_id
    response_str = gt.get_response_str()
    return HttpResponse(response_str)

  urls.py

from django.conf.urls import url
from app01 import views
urlpatterns = [
    url(r''^admin/'', admin.site.urls),
  url(r''^login/'', views.login),
    # 极验滑动验证码 获取验证码的url
    url(r''^pc-geetest/register'', views.get_geetest),
]

  login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>欢迎登录</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/mystyle.css">
</head>
<body>

<div>
    <div>
        <form>
            {% csrf_token %}
            <div>
                <label for="username">用户名</label>
                <div>
                    <input type="text"id="username" name="username" placeholder="用户名">
                </div>
            </div>
            <div>
                <label for="password">密码</label>
                <div>
                    <input type="password"id="password" name="password" placeholder="密码">
                </div>
            </div>
            <div>
                <!-- 放置极验的滑动验证码 -->
                <div id="popup-captcha"></div>
            </div>
            <div>
                <div>
                    <button type="button"id="login-button">登录</button>
                    <span></span>
                </div>
            </div>
        </form>
    </div>
</div>

<script src="/static/jquery-3.3.1.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
<!-- 引入封装了failback的接口--initGeetest -->
<script src="http://static.geetest.com/static/tools/gt.js"></script>
<script>

    // 极验 发送登录数据的
    var handlerPopup = function (captchaObj) {
        // 成功的回调
        captchaObj.onSuccess(function () {
            var validate = captchaObj.getValidate();
            // 1. 取到用户填写的用户名和密码 -> 取input框的值
            var username = $("#username").val();
            var password = $("#password").val();
            $.ajax({
                url: "/login/", // 进行二次验证
                type: "post",
                dataType: "json",
                data: {
                    username: username,
                    password: password,
                    csrfmiddlewaretoken: $("[name=''csrfmiddlewaretoken'']").val(),
                    geetest_challenge: validate.geetest_challenge,
                    geetest_validate: validate.geetest_validate,
                    geetest_seccode: validate.geetest_seccode
                },
                success: function (data) {
                    console.log(data);
                    if (data.status) {
                        // 有错误,在页面上提示
                        $(".login-error").text(data.msg);
                    } else {
                        // 登陆成功
                        location.href = data.msg;
                    }
                }
            });
        });

         $("#login-button").click(function () {
            captchaObj.show();
        });
        // 将验证码加到id为captcha的元素里
        captchaObj.appendTo("#popup-captcha");
        // 更多接口参考:http://www.geetest.com/install/sections/idx-client-sdk.html
    };
    // 当input框获取焦点时将之前的错误清空
    $("#username,#password").focus(function () {
        // 将之前的错误清空
        $(".login-error").text("");
    });

    // 验证开始需要向网站主后台获取id,challenge,success(是否启用failback)
    $.ajax({
        url: "/pc-geetest/register?t=" + (new Date()).getTime(), // 加随机数防止缓存
        type: "get",
        dataType: "json",
        success: function (data) {
            // 使用initGeetest接口
            // 参数1:配置参数
            // 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件
            initGeetest({
                gt: data.gt,
                challenge: data.challenge,
                product: "popup", // 产品形式,包括:float,embed,popup。注意只对PC版验证码有效
                offline: !data.success // 表示用户后台检测极验服务器是否宕机,一般不需要关注
                // 更多配置参数请参见:http://www.geetest.com/install/sections/idx-client-sdk.html#config
            }, handlerPopup);
        }
    })


</script>
</body>
</html>

  

知识储备:python中chr() 函数 和 ord() 函数的用法。

1,chr()函数

格式: Chr(<数值表达式>)

说明:chr() 用一个范围在 range(256)内的(就是0~255)整数做参数。函数返回值类型为String,其数值表达式值取值范围为0~255,返回一个对应的字符(对应的ASCII字符)。

例如:Print(Chr(78))  结果显示:N

 

65-90  大写A-Z

97-122 小写的a-z

2,ord() 函数

格式: ord("字符串")

说明:函数返回值类型为int

例如:Print(ord(''0''))  结果显示:48

 

 

 

参考文献:https://www.cnblogs.com/chimeiwangliang/p/7130434.html

原文出处:https://www.cnblogs.com/wj-1314/p/10971846.html

Golang 学习笔记(03)——os包(1)

Golang 学习笔记(03)——os包(1)

package main

import (
    "os"
    "fmt"
    "time"
    "strings"
)

func main(){
    //获取主机名
    fmt.Println(os.Hostname());
 
    //获取当前目录
    fmt.Println(os.Getwd());
  
    //获取用户ID
    fmt.Println(os.Getuid());
  
    //获取有效用户ID
    fmt.Println(os.Geteuid());
  
    //获取组ID
    fmt.Println(os.Getgid());
  
    //获取有效组ID
    fmt.Println(os.Getegid());
  
    //获取进程ID
    fmt.Println(os.Getpid());
  
    //获取父进程ID
    fmt.Println(os.Getppid());
  
    //获取环境变量的值
    fmt.Println(os.Getenv("GOPATH"));
  
    //设置环境变量的值
    os.Setenv("TEST", "test");
  
    //改变当前工作目录
    os.Chdir("C:/");
    fmt.Println(os.Getwd());
  
    //创建文件
    f1, _ := os.Create("./1.txt");
    defer f1.Close();
  
    //修改文件权限
    if err := os.Chmod("./1.txt", 0777); err != nil {
       fmt.Println(err);
    }
  
    //修改文件所有者
    if err := os.Chown("./1.txt", 0, 0); err != nil {
       fmt.Println(err);
    }
  
    //修改文件的访问时间和修改时间
    os.Chtimes("./1.txt", time.Now().Add(time.Hour), time.Now().Add(time.Hour));
  
    //获取所有环境变量
    fmt.Println(strings.Join(os.Environ(), "\r\n"));
  
    //把字符串中带${var}或$var替换成指定指符串
    fmt.Println(os.Expand("${1} ${2} ${3}", func(k string) string {
       mapp := map[string]string{
          "1": "111",
          "2": "222",
          "3": "333",
       };
       return mapp[k];
    }));
  
    //创建目录
    os.Mkdir("abc", os.ModePerm);
  
    //创建多级目录
    os.MkdirAll("abc/d/e/f", os.ModePerm);
  
    //删除文件或目录
    os.Remove("abc/d/e/f");
  
    //删除指定目录下所有文件
    os.RemoveAll("abc");
  
    //重命名文件
    os.Rename("./2.txt", "./2_new.txt");
  
    //判断是否为同一文件
    //unix下通过底层结构的设备和索引节点是否相同来判断
    //其他系统可能是通过文件绝对路径来判断
    fs1, _ := f1.Stat();
    f2, _ := os.Open("./1.txt");
    fs2, _ := f2.Stat();
    fmt.Println(os.SameFile(fs1, fs2));
  
    //返回临时目录
    fmt.Println(os.TempDir());
}

JavaEE_SSH_Spring学习笔记(9)----让Spring自动扫描和管理Bean

JavaEE_SSH_Spring学习笔记(9)----让Spring自动扫描和管理Bean

  1. 源地址 http://blog.chinaunix.net/uid-21227800-id-65924.html

  2. package com.szy.spring.service;   
  3.   
  4. import org.springframework.stereotype.Service;   
  5.   
  6. import com.szy.spring.dao.PersonDao;   
  7. @Service("service")   
  8. public class UserServiceImpl implements UserService   
  9. {   
  10.     private PersonDao personDaoBean;   
  11.        
  12.     public void show()   
  13.     {   
  14.         personDaoBean.show();   
  15.     }   
  16.   
  17.     public void setPersonDaoBean(PersonDao personDaoBean)   
  18.     {   
  19.         this.personDaoBean = personDaoBean;   
  20.     }   
  21. }  

 在前面的例子中,都是使用XML的bean定义来使用组件,在大的项目中,通常会有上百个组件,如果这些组件采用xml的bean定义来配置,显然会使配置文件显得很臃肿,查找和维护起来不方便。Spring2.5为我们引入了组件自动扫描机制,它可以在类路径下寻找标记了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入到spring容器中管理,它的作用和在xml中使用bean节点配置组件一样。要使用自动扫描机制,我们需要把配置文件如下配置:

Xml代码
  1. xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xmlns:context="http://www.springframework.org/schema/context"  
  5.     xmlns:tx="http://www.springframework.org/schema/tx"  
  6.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
  7.                 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd   
  8.                 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">  
  9.     <context:component-scan base-package="com.szy.spring">context:component-scan>  
  10. beans>  

 其中base-package为需要扫描的包(包括子包)

@Service用于标注业务层的组件,@Controller用于标注控制层组件(如struts中的action),@Repository用于标注数据访问组件,即DAO组件,而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。但是在目前的spring版本中,这几个注解的作用是一样的,但是在以后可能会进行区分。

 

下面把先前的例子修改一下:

首先是PersonDaoBean类,修改如下

Java代码
  1. package com.szy.spring.dao;   
  2.   
  3. import org.springframework.stereotype.Repository;   
  4.   
  5. @Repository  
  6. //告诉spring这个类要交给spring管理,   
  7. public class PersonDaoBean implements PersonDao   
  8. {   
  9.     public void show()   
  10.     {   
  11.         System.out.println("执行PersonDaoBean中的add()方法");   
  12.     }   
  13. }  

然后是UserServiceImpl类

Java代码
  1. package com.szy.spring.service;   
  2.   
  3. import org.springframework.stereotype.Service;   
  4.   
  5. import com.szy.spring.dao.PersonDao;   
  6. @Service  
  7. //把这个类交给spring管理,作为服务了。   
  8. public class UserServiceImpl implements UserService   
  9. {   
  10.     private PersonDao personDaoBean;   
  11.        
  12.     public void show()   
  13.     {   
  14.         personDaoBean.show();   
  15.     }   
  16.   
  17.     public void setPersonDaoBean(PersonDao personDaoBean)   
  18.     {   
  19.         this.personDaoBean = personDaoBean;   
  20.     }   
  21.   
  22.     public PersonDao getPersonDaoBean()   
  23.     {   
  24.         return personDaoBean;   
  25.     }   
  26. }  

 下面我们进行测试,原来的测试代码是userServiceImpl

Java代码
  1. ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");   
  2.         UserService service=(UserService)ctx.getBean("userService");   
  3.         service.show();  

其中userService是我们在配置文件中配置的bean的id。但是如今我们并没有id这个属性,在spring2.5中,默认的id是类的名称,但是开后是小写,也就是userServiceImpl,因此测试代码应修改如下:

Java代码
  1. AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");   
  2.         UserService service=(UserService)ctx.getBean("userServiceImpl");   
  3.         System.out.println(service);  

如果我们想自己命名的话,则只需在注解后加上括号,里面写入你希望的名字,如

@Service("userService")。

 

在spring中默认的是之生成一个bean实例,如果我们想每次调用都产生一个实例,则标注需如下配置

@Service @Scope("prototype")

 

在xml中我们还可以配置初始化方法和销毁方法,使用标注后只需如下标注

Java代码
  1. @PostConstruct  
  2.     public void init()   
  3.     {   
  4.         System.out.println("初始化");   
  5.     }   
  6.     @PreDestroy  
  7.     public void destory()   
  8.     {   
  9.         System.out.println("销毁");   
  10.     }  

 

 使用注解后,我们的xml文件变得十分简单,因此建议大家在以后的开发中使用注解。

  • spring.rar (2.6 MB)

今天关于Spring学习笔记11——bean之间的关系的介绍到此结束,谢谢您的阅读,有关Django学习笔记(11)——开发图书管理页面、Django学习笔记(17)——BBS+Blog项目开发(1)验证码功能的实现、Golang 学习笔记(03)——os包(1)、JavaEE_SSH_Spring学习笔记(9)----让Spring自动扫描和管理Bean等更多相关知识的信息可以在本站进行查询。

本文标签: